Hatena::ブログ(Diary)

sirocco の書いてもすぐに忘れるメモ

2014-03-24

正常にパース出来た場合はJust、失敗した場合はNothingを返すmaybeRead

| 17:59 |

read関数文字列から指定された型のデータを生成します。

型が指定されない場合、read関数はどんな型に変換すれば良いか分からないので例外を発生します。

また、パースにエラーがある場合にも、例外を発生します。

> read "123"::Int
-- > 123
-- 型が指定されない場合
> read "3.14"

<interactive>:86:1:
    No instance for (Read a0) arising from a use of `read'
    The type variable `a0' is ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there are several potential instances:
      instance Read GHC.IO.Exception.ExitCode
        -- Defined in `GHC.IO.Exception'
      instance Read () -- Defined in `GHC.Read'
      instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
      ...plus 44 others
    In the expression: read "3.14"
    In an equation for `it': it = read "3.14"
-- パースにエラーがある場合
> read "abc"::Int
*** Exception: Prelude.read: no parse
  • 例外を発生させるのではなく失敗を含むMaybe型を返して欲しい

例外を補足し、正常にパース出来た場合はJust、パースに失敗した場合はNothingを返すmaybeReadの例がStack Overflowにありました。

maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
    [(x, "")] -> Just x
    _         -> Nothing
> maybeRead "abc"::Maybe Int
-- > Nothing
> maybeRead "123"::Maybe Int
-- > Just 123

Data.Time Data.Time.Calendar>  maybeRead "2000-01-01"::Maybe Day
-- > Just 2000-01-01
Data.Time Data.Time.Calendar>  maybeRead "abc"::Maybe Day
-- > Nothing
  • reads 関数が重要な役目を持っているようです。

reads 関数を少しいじってみます。

>  reads "123" ::[(Int,String)]
-- > [(123,"")]
>  reads "123 456" ::[(Int,String)]
-- > [(123," 456")]
>  reads "123abc" ::[(Int,String)]
-- > [(123,"abc")]
>  reads "2014-03-24hello,world!" ::[(Day,String)]
-- > [(2014-03-24,"hello,world!")]
  • try により Either型を返す。
Prelude Control.Exception> try (print (read "123"::Int)) ::IO (Either SomeException ())
123
-- > Right ()

Prelude Control.Exception> try (print (read "qwe"::Int)) ::IO (Either SomeException ())
-- > Left Prelude.read: no parse

Prelude Control.Exception> try (evaluate (read "123"::Int)) ::IO (Either SomeException Int)
-- > Right 123
Prelude Control.Exception> try (evaluate (read "1q23"::Int)) ::IO (Either SomeException Int)
-- > Left Prelude.read: no parse
main = do
    result <- tryJust selectDivByZero (evaluate $ 5 `div` 0)
    case result of
        Left what -> putStrLn $ "Division by " ++ what
        Right val -> putStrLn $ "The answer was: " ++ show val
  where
    selectDivByZero :: ArithException -> Maybe String
    selectDivByZero DivideByZero = Just "zero"
    selectDivByZero _ = Nothing

2014-01-13

GHC の ScopedTypeVariables オプション

| 17:44 |

Rosetta Code にあるDetect division by zeroコンパイルしてみたのですが、GHC 7.6.3ではエラーになってしまいます。

出力されたエラーメッセージで検索すると arising from a use of `Control.Exception.catch’ がみつかりました。

{-# LANGUAGE ScopedTypeVariables #-}

を先頭に書くかGHCi で

> :set -XScopedTypeVariables

を指定する必要があります。

(e :: C.SomeException)を指定しないとエラーになりました。

{-# LANGUAGE ScopedTypeVariables #-}
import qualified Control.Exception as C

check x y = C.catch (x `div` y `seq` return False) (\(e :: C.SomeException) -> return True)

Main> check 1 3 -- > False
Main> check 1 0 -- > True

2013-12-27

GHCi runtime linker: fatal error: I found a duplicate definition for symbol

| 13:10 |

Windows 8.1GHCの環境を構築しているのですが、

GHCi runtime linker: fatal error: I found a duplicate definition for symbol

というエラーが出るようになってしまいました。

エラー・メッセージで検索したサイトのひとつに次のサイトがありました。

GHCi runtime linker: fatal error: I found a duplicate definition for symbol 出現時のメモを見ますと異なるバージョンのパッケージが二つ入っているということです。

Key型には hash を求めるインスタンスを定義してあります。

data Key = Key Int String deriving (Eq,Show)

instance Hashable Key where
   hashWithSalt s (Key a b) = s `hashWithSalt` a `hashWithSalt` b

GHCiでKey型を表示しようとするとと、hashable-1.1.2.5を読み込んで、さらにhashable-1.2.1.0を読み込んだ時点でエラーになっています。

GHCi runtime linker: 
*Main> Key 1"hello"
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package primitive-0.5.0.1 ... linking ... done.
Loading package vector-0.10.0.1 ... linking ... done.
Loading package bytestring-0.10.0.2 ... linking ... done.
Loading package text-0.11.3.1 ... linking ... done.
Loading package hashable-1.1.2.5 ... linking ... done.
Loading package hashable-1.2.1.0 ... 

GHCi runtime linker: fatal error: I found a duplicate definition for symbol
   _hashable_fnv_hash
whilst processing object file
   C:\Users\sirocco\AppData\Roaming\cabal\i386-windows-ghc-7.6.3\hashable-1.2.1.0\libHShashable-1.2.1.0.a
This could be caused by:
   * Loading two different object files which export the same symbol
   * Specifying the same object file twice on the GHCi command line
   * An incorrect `package.conf' entry, causing some object to be
     loaded twice.
GHCi cannot safely continue in this situation.  Exiting now.  Sorry.

この問題を解決するために、古い hashable-1.1.2.5 をunregisterします。

$ ghc-pkg unregister hashable-1.1.2.5
ghc-pkg.exe: unregistering hashable-1.1.2.5 would break the following packages: 
unordered-containers-0.2.3.0 haskell-platform-2013.2.0.0 case-insensitive-1.0.0.1 
hashtables-1.1.2.1 HStringTemplate-0.7.1 void-0.6.1 semigroups-0.12.1 (use --force 
to override)

(use --force to override) と言われるので "--force" をつけてやりなおし。

$ ghc-pkg --force unregister hashable-1.1.2.5
unregistering hashable-1.1.2.5 would break the following packages: unordered-
containers-0.2.3.0 haskell-platform-2013.2.0.0 case-insensitive-1.0.0.1 hashtables-
1.1.2.1 HStringTemplate-0.7.1 void-0.6.1 semigroups-0.12.1 (ignoring)

Data.HashTable.IO が読めないエラーが発生してしまいます。

ghc-pkg list でみると hashtables-1.1.2.1がunregisterになってしまったようです。

C:\Users\sirocco\AppData\Roaming\ghc\i386-mingw32-7.6.3\package.conf.d:
    Cabal-1.18.1.2
    {HStringTemplate-0.7.1}
    blaze-builder-0.3.3.2
    containers-0.4.2.1
    datetime-0.2.1
    hashable-1.2.1.0
    {hashtables-1.1.2.1}
    nats-0.1.2
    safe-0.3.3
    {semigroups-0.12.1}
    strict-0.3.2
    utf8-string-0.3.7
    {void-0.6.1}
    wx-0.13.2.3
    wxcore-0.13.2.3
    wxdirect-0.13.1.3
    wxdirect-0.90.0.1

hashable-1.2.1.0をunregisterして再度 hashtables をインストールするとOKでした。

$ ghc-pkg unregister hashable-1.2.1.0
Main> Key 1"abc"
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package primitive-0.5.0.1 ... linking ... done.
Loading package vector-0.10.0.1 ... linking ... done.
Loading package bytestring-0.10.0.2 ... linking ... done.
Loading package text-0.11.3.1 ... linking ... done.
Loading package hashable-1.2.1.0 ... linking ... done.
Loading package hashtables-1.1.2.1 ... linking ... done.
Key 1 "abc"
Main> hash (Key 1"abc")
-1314789693

参考

2013-12-03

HaskellからCの配列を利用する

| 07:13 |

ITプロに「本物のプログラマHaskellを使う」が連載されていますが、今回の記事はは「第58回 Cの配列をHaskellで利用する」です。

丁度配列を使いたいところでした。しかし、Array 型は、参照は O(1) で出来ますが、 更新には O(n) かかります。

そこで、記事を写経しながら、Cの配列を使う練習をしてみました。

  • Array.hs
-- $ ghc -Wall carray.h carray.c Array.hs
import Foreign.C.Types       ( CInt(..) )
import Foreign.Marshal.Array
import Foreign.Ptr           ( Ptr(..) )

main :: IO ()
main = do
    let xs :: [CInt]
        xs  = [0..9]
        len = length xs
    ys <- withArray xs $ \carr -> do
              cUpdateArray carr 0 (fromIntegral 123)
              cUpdateArray carr 4 (fromIntegral 999)
              print =<< cGetArrayElem carr 0  -- > 123
              print =<< cGetArrayElem carr 1  -- > 1
              print =<< cGetArrayElem carr 2  -- > 2
              print =<< cGetArrayElem carr 3  -- > 3
              print =<< cGetArrayElem carr 4  -- > 999
              peekArray len carr
    print ys  -- > [123,1,2,3,999,5,6,7,8,9]

foreign import ccall "updateArray"  cUpdateArray  :: Ptr CInt -> CInt -> CInt -> IO ()
foreign import ccall "getArrayElem" cGetArrayElem :: Ptr CInt -> CInt -> IO CInt
  • carray.h
void updateArray ( int*, int, int );
int  getArrayElem ( int *, int);
  • carray.c
#include "carray.h"

void updateArray ( int *xs, int p, int x) { xs[p] = x;}

int  getArrayElem ( int *xs, int p) { return xs[p];}
$ ghc carray.h carray.c Array.hs
[1 of 1] Compiling Main             ( Array.hs, Array.o )
Linking Array.exe ...

$ ./Array
123
1
2
3
999
[123,1,2,3,999,5,6,7,8,9]

2013-12-01

with を使って C の関数へポインタ渡しをする

| 06:14 |

Foreign.Marshal.UtilsのwithはC の関数への引数ポインタ渡しするときに使う。

-- ghc --make -Wall frexp.hs  test.c  -o frex
{-# LANGUAGE ForeignFunctionInterface #-}
 
import Foreign.Ptr
import Foreign.C.Types
import Foreign.Marshal.Utils

foreign import ccall unsafe "test" c_test :: Ptr CInt -> Ptr CInt -> IO CInt

func :: CInt -> CInt -> IO CInt
func x y = with x  (\xptr -> with y (\yptr -> c_test xptr yptr))

main :: IO()
main = print =<< func 2 3 -- => 8
  • test.c
int test(int *a, int *b){ return (*a + (*b *2)); }