ホームに戻る
 C言語がわかる人のためのHaskell講座

0、はじめに

C言語を書ける人にわかりやすいように、
Haskellの説明を書いてみました。

工夫として、
「改訂新C言語入門 シニア編 著:林晴比古」
の説明順序と同じにして説明を書きました。

よって、C言語をどうHaskellに置き換えるか?
という点に重きが置かれており、
Haskell独自の記法の面白さがわからない、
かもしれません。

よって、最後に「19、追記」で補足しました。

1、Hackellの基本的な知識

Haskell は純粋関数型言語です。
手続き型では無いのでfor文やif文のような流れがありません。
関数型なので変数は存在せずすべてが関数です。

コンパイルは以下のようにする。

ghc sample.hs 

コメントは次のように書く。

{- コメント -}

Hello!World! は次のようにする。

main = putStrLn "Hello,World!"

コードは以下のように () を省略できる。

main = print (2 * (3 + 4))

main = print $ (*) 1 $ 2 + 3

2行に渡るときには以下のように書ける。
このときインデントの位置は重要。

main = do putStrLn "abc"
          putStrLn "def"

2、定数

8進数は頭に 0O と書くが、
1文字目はゼロで2文字目は英字のオー。

真偽値:True, False
8進数:0o37, 0O37
10進数:-123, 123
16進数:0xaf, 0xAF, 0xAF, 0XAF
浮動小数:1.1::Float, 1.1e3::Float
倍浮動小数:1.1, 1.1e3, 1.2::Double

文字定数:'a', 'A'
文字列定数:"abc", "ABC"

Int:-2147483648〜2147483647
Integer:制限なし

エスケープシーケンス
\t, \n, \r, \v, \f, \a, \b, \', \", \\

3、データ型

Bool:真偽値
Int:32ビット整数
Integer:整数
Float:浮動小数
Double:倍浮動小数
Char:文字型
String:文字列型。[Char]と同じ

型名の変換

type Name = String

4、配列と文字列

配列は次のように書く。0インデックス。

[0, 1, 2, 3, 4]

1番目の要素

[0, 1, 2, 3, 4] !! 1

要素数

length [0, 1, 2, 3, 4]

文字列

"abc" は ['a', 'b', 'c'] と同じ

ヌル文字 '\0' は存在しますが、
文字列の終端文字 '\0' の概念はありません。

5、型変換

fromIntegral:IntegralからNumに変換
realToFrac:RealからFractionalに変換

Integral(Int, Integer)
Num(Integer、Int、Float、Double)
Real(Int、Integer、Float、Double)
Fractional(Float,Double)

例えば Float型の 1.2 を Double型に変換するには、

((realToFrac (1.2::Float))::Double)

のようにする。

ちなみに Integral などは型クラスであり型では無い。

6、記憶クラス

スコープは関数単位になる。

7、初期化

Haskell は再代入は認められないが初期化はできる。
次のようにする。

a = 5

これは変数 a に 5 を初期化して入れてるように見えるが、
Haskell では単に 5 という数字を返す関数 a である。

8、演算子

Cとは / の使い方が異なる。
Cでいう / と % は `div` と `mod` を使う。
!= は使えず /= を使う。

+, -, *, / :四則演算 ただし / は浮動小数の計算になる。
`div`, `mod` :整数値について商と余り
==, <, >, <=, >=, /= :比較演算子(/= は「等しくない」)
&&, || :論理積、論理和
^, ^^ :累乗(^ は整数、^^ は浮動小数)
.&., .|., `xor`, complement :ビット演算子
`shiftL`, `shiftR`, `rotateL`, `rotateR` :ビットシフト

9、制御文

if 文は if 式で書くことができる。

if (x == 1) then True else False

for 文は再帰で書く。
(x はリストの先頭、 xs はそれ以降)

factorial :: [Int] -> Int
factorial [] = 1
factorial (x:xs) = x * factorial xs

main = print $ factorial [1..10]

上の再帰はスタックをたくさん使用します。
よって、次のような末尾再帰にすると良い。
(where によって f は factorial 内からしか使えない。)

factorial :: [Int] -> Int
factorial n = f n 1
  where
    f [] s = s
    f (x:xs) s = f xs (x * s)

main = print $ factorial [1..10]

break の実装

factorial :: [Int] -> Int
factorial n = f n 1
  where
    f [] s = s
    f (x:xs) s
      | x == 5 = s
      | otherwise = f xs (x * s)

main = print $ factorial [1..10]

continue の実装

factorial :: [Int] -> Int
factorial n = f n 1
  where
    f [] s = s
    f (x:xs) s
      | x == 5 = f xs s
      | otherwise = f xs (x * s)

main = print $ factorial [1..10]

switch の実装

isTwo :: Int -> Bool
isTwo x = case x of 
              2 -> True
              _ -> False

10、ポインタ

Haskell にポインタはありません。

11、関数

Int型の a と Int型の b を掛け算してInt型で返す関数は次のように書ける。

f::Int->Int->Int
f a b = a * b

12、構造体

data Dat = D{s::String, i::Int}

dat1::Dat
dat1 = D "abc" 123

使用する場合は i dat1 や s dat1 と書く。

13、ビットフィールド

.&., .|., `xor`, complement :ビット演算子
`shiftL`, `shiftR`, `rotateL`, `rotateR` :ビットシフト

ビット演算には「import Data.Bits」が必要

import Data.Bits

m, n :: Int
m = 7
n = 2
main = print $ (m .&. (complement n))

14、共用体

Haskell には共用体的なものが存在しますが、
実際には C の共用体とは異なるものです。

15、プリプロセッサ

Haskell では C スタイルのマクロが使える。

{-# OPTIONS_GHC -cpp #-}
#define NUM 5

main = print $ NUM

16、コンソール入出力

getChar, getLine, getContents:入力
putChar, putStr, putStrLn:出力

main = do name <- getLine
          putStrLn name

17、入出力以外の標準関数

abs:絶対値
max:大きいほうの値
min:小さいほうの値
sin, cos, tan:三角関数

乱数

import System.Random

rand :: Int -> Int -> IO Int
rand x y = getStdRandom (randomR (x, y))

main = do n <- rand 1 6
 	  print n

http://www.haskell.org/ghc/docs/latest/html/libraries/

18、ファイル処理関数

writeFile, appendFile, readFile:ファイル入出力

main = do text <- readFile "a.txt"
          writeFile "b.txt" text

19、追記

データ型を作成できる。

data Type = A | B

タプルと呼ばれるデータ型が存在する。

(0, "abc")

型クラスが存在する。

class ClassA a where
  fa :: a -> String
  fa _ = "Error"

data A = A String

instance ClassA A where
  fa(A s) = "String:" ++ s

main = print $ fa(A "abc")

リストの操作関数が充実している。

null []:空リストであれば True を返す
[1..]:正の整数のリスト(無限リストが可能)
0 : [1..5]:0 から 5 のリスト
[1..2]++[3..10]:1 から 10 のリスト
[x | x <- [1.. 5], x < 3 && x >= 5]:1, 2, 5 のリスト
length ['a'..'d']:リストの要素数を返す
head [0..6]:先頭の 0 を返す
tail [0..6]:[1..5] を返す
take 3 [1..5]:最初の3要素をリストで得る

import Data.List

reverse [1, 2, 3]:逆転
sort [3, 2, 1]:昇順ソート
map f [1, 2, 3]:すべてに関数 f を適応
any f [0, 1]:関数 f でひとつでも True であれば True
all f [0, 1]:関数 f ですべて True であれば True

main = print $ map (\ x -> x + 1) [1, 2]
main = print $ any (\ x -> x == 1) [1, 2]

ローカル関数を定義できる

ローカル関数 n1, n2 を定義している。

f x = let
         n1 = x + 1
         n2 = x + 2
       in
         n1 * n2

関数は定義を省くことができる。

main = print $ (\a b -> a * b) 2 3

遅延評価関数

次のような自作の if が書けます。
n == 3 であれば 1 になり そうでなければ 0 になる。

myIf :: Bool -> a -> a -> a
myIf True t f = t
myIf False t f = f

n = 2

main = print $ myIf (n == 3) 1 0

for 文は必ず末尾再帰を用いるわけではない。

再帰を使わずとも foldl, filter, takeWhile などで実装可能な場合がある。
foldl:左から演算
filter:条件を満たすものに限り
takeWhile:条件を満たす間は。満たさなくなったら打ち切り。

main = print $ foldl (*) 1
             $ filter (/= 3)
             $ takeWhile (< 5) [1..10]

モジュールを作成し import で使用できる。

モナドが使用できる。

inserted by FC2 system