haskellの覚書

 
tiutiu.net/ プログラム/language/haskell/
2007/7/8
本家と実装

本家はhttp://www.haskell.org/。 実装はいくつかあるが, ここではGHCを使用。GHCのWebページは http://haskell.org/ghc/

GHCにはWindows用のインストーラが用意されていて, インストールも インストーラを実行するだけで簡単です。実行後は環境変数にGHCのbinへの パスを設定しておきます。

putStrとコンパイル

即時実行

$ 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などがそれっぽいがいい例が見つからない。

OpenGL
準備

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を参考にさせていただいた。


Google