haskellの覚書
本家はhttp://www.haskell.org/。 実装はいくつかあるが, ここではGHCを使用。GHCのWebページは http://haskell.org/ghc/。
GHCにはWindows用のインストーラが用意されていて, インストールも インストーラを実行するだけで簡単です。実行後は環境変数にGHCのbinへの パスを設定しておきます。
即時実行
$ ghc -e 'putStr "Hello, haskell world!"' Hello, haskell world!$
コンパイルするなら, hello.hsというファイルに次の内容を 書きます。
main = putStr "Hello, haskell world!"
コンパイルは次のとおり。
$ ghc --make hello.hs [1 of 1] Compiling Main ( hello.hs, hello.o ) Linking hello.exe ...
これでhello.exeというのが作成される。 hello.exeを実行すると, Hello, haskell world!と表示される。
ソースコードは次のようにしてコンパイル することなしに実行することができる。
$ ghc -e main hello.hs Hello, haskell world!$
もし, hello.hsで関数mainの名前をfooに替えていたら, つまり 次のようなソースコード。
foo = putStr "Hello, haskell world!"
この場合は次のようにすることで即時実行が可能となる。
$ ghc -e foo hello.hs
あるいは
$ ghc -e Main.foo hello.hs
Mainではなくmainとした場合はエラーになって, Not in scope: `main'と 言われることから, Mainというスコープの中にfooが定義されるのでしょう。
行の先頭に--と書くとコメントのようです。
ghciやghc --interactiveやスタートメニューのGHCiを実行 すると, インタラクティブモードになります。
終了は":quit"です。":help"でヘルプが出ます。
-- 文字の出力 putChar 'a' -- 文字列の出力 putStr "Hello, haskell world!" -- 文字列の出力, 改行付き putStrLn "Hello, haskell world!" -- 数字の出力 print 1 -- 数字の出力2 print (-2)
どうも負の数字はそのままでは-が何かに引っかかるようだ。
基本的に一行で定義。引数は空白で区切る。 呼び出しも空白で区切る。
g x y = x^2 + 2*x*y + y^2 main = print (g 1 2)
mainは引数がない関数である。
複数行持ちたい場合はdoを使用する。
main = do putStr "Hello, " putStrLn "Haskell World!"
ここでインデントは重要で, インデントが深くなると関数の引数と みなされ, インデントが浅くなるとdoに入らない別の式とみなされる。
f x = if x < 0 then -x else x main = print f(-5)
f x = if x==0 then 1 else x*f(x-1) main = print(f(4))
どうもCでいうforやwhileの類の構文はないらしい。
-- [1..10] = [1,2,3,4,5,6,7,8,9,10] f a = do if length a == 0 then return 0 else do print (head a) f (tail a) main = f [1..10]
繰り返しはないがリストはある。headはlispでいうcarで, tailはcdr。
ifとthen, elseのインデントも厳しく, thenはifより深くないと いけない。elseも同様。でもelseがthenより浅いのはいいようだ。
リストのn番目にアクセスしたいときは, !!が使える。
$ ghc -e 'print ([1,2,3] !! 1)' 2
main = do s <- getLine putStr s
他にgetCharも使えます。<-はIOがらみのときだけ使うようです。
readが本質です。
import System.IO main = do -- 標準出力のバッファリングをオフ hSetBuffering stdout NoBuffering putStr("Input number: ") a <- getLine print (read a::Integer)
自分の環境だとどうしてもInput numberと表示されるより 入力が求められるほうが早かったので, 標準出力のバッファリングを オフにしてあります。そのため関数がhSetBufferingで, これとstdout, NoBufferingというのを使うにはSystem.IOをimportする必要が あります。
引数はgetArgsから読み取る。getArgsを使うにはSystem( getArgs )を importしておく必要がある。
import System( getArgs ) main = do args <- getArgs if length args/=2 then usage else do let a = read (head args)::Integer let b = read (head (tail args))::Integer print (a + b) usage = putStrLn "add number number"
/=はnot equalの意味。
フィードバックはこちらのブログへ
mainがないファイルではモジュールを定義し, mainがあるファイルではそのモジュールを呼び出す。
mainのあるファイルmain.hs
-- main.hs import Foo( f ) main = print (f 1)
mainのないファイルfoo.hsではモジュールFooを定義。
-- foo.hs module Foo where f x = x + 1
コンパイル方法は以下のとおり
$ ghc --make main.hs foo.hs
オブジェクトファイルを作るなら
$ ghc -c foo.hs $ ghc -c main.hs $ ghc main.o foo.o
オブジェクト同士の結合の時は, --makeオプションは不要。
もし, foo.hsがfooというディレクトリにあるなら, -iオプションでfooというディレクトリも見なさいと 命令する。例は以下のとおり。
$ ghc -ifoo --make main.hs foo/foo.hs
-- readfile.hs import IO; -- openFile, ReadMode, hGetContents, hClose main = do hfile <- openFile "readfile.hs" ReadMode text <- hGetContents hfile putStrLn text hClose hfile
この場合, readfile.hsの内容全てが書かれる。
-- readfile2.hs import IO; main = do hfile <- openFile "readfile2.hs" ReadMode text <- hGetLine hfile putStrLn text hClose hfile
こうすれば, 一行づつ読み込める。
単に全部読み込むだけならreadFileでもOK。
-- readfile3.hs import IO; main = do text <- readFile "readfile3.hs" putStrLn (head (lines text))
EOFと一文字づつの読み込みの例は同時に。
-- readfile4.hs import IO; myreadfile handle = do b <- hIsEOF handle if b then return () else do c <- hGetChar handle putChar c myreadfile handle -- if hIsEOF handleとはhIsEOFが:: Handle -> IO Bool -- なので書けないようだ。 main = do f <- openFile "readfile4.hs" ReadMode myreadfile f
http://www.haskell.org/onlinereport/io.html に詳しい。ただ, これを見るとstatに相当するものが見当たらない。
-- env.hs import System.Environment -- getEnv main = do stros <- getEnv "OS" putStrLn stros -- putStrLn (getEnv "OS")は型がStringとIO Stringで異なるので不可。
Network.Socketを使えばできるようだ。 mailの送信などはNetwork.Emailにあるが, いい例が見つからない。
Data.Bitsなどがそれっぽいがいい例が見つからない。
http://www.xmission.com/~nate/glut.htmlから glut-3.7.6-bin.zipをダウンロードしてきて, 解凍する。 中身のglut32.dllをC:\Windows\System32にコピー。
openGL GLUTを使用したほぼ最小の例。コンパイルに特別なオプションはいりません。 ghc --make helloworld.hsでOK。 なお, わかって書いていない部分が多々ある。 終了はCtrl+Cで強制終了してください。エクスプローラーからダブルクリック で実行した場合は, 一緒に立ち上がるコンソールを殺せばOK。
-- simple.hs import Graphics.Rendering.OpenGL import Graphics.UI.GLUT display = swapBuffers main = do initialDisplayMode $= ([ DoubleBuffered, RGBMode ]) createWindow "simple" displayCallback $= display mainLoop
初期窓のサイズなどを決定するために, initialWindowSize, initialWindowPositionという関数があることを付け加えておく。
ウィンドウを閉じてもプロセスが生きているのはあまりよくないので, せめてqを押すと全て終了する例。
-- simple_exit import Graphics.Rendering.OpenGL import Graphics.UI.GLUT import System.Exit -- exitWith ExitSuccess display = swapBuffers keyboardMouse c _ _ _ = case c of Char 'q' -> exitWith ExitSuccess _ -> return () main = do initialDisplayMode $= ([ DoubleBuffered, RGBMode ]) createWindow "simple_exit" displayCallback $= display keyboardMouseCallback $= Just keyboardMouse mainLoop
http://haskell.org/hawiki/HaskellOpenGlを参考にさせていただいた。