LoginSignup
12
6

More than 5 years have passed since last update.

「Haskell入門ハンズオン!」事前資料 - 3/3

Last updated at Posted at 2017-06-21

やってみよう

「Haskell入門ハンズオン!」事前資料の一覧

  1. 概要
  2. 処理系の導入と対話環境
  3. やってみよう

ファイルに保存した定義を読みこむ

ファイルに保存した定義を、対話環境に読みこんでみる。まずは、好きなエディタ(例ではVim)で、ファイルを作成する。

% vim fruits.hs
myFavoriteFruit = "apple"

これを、対話環境に読みこむ。

% stack ghci
Prelude> :load fruits.hs
*Main>

値を表示してみよう。

*Main> myFavoriteFruit
"apple"

対話環境はそのままにして、別のターミナルでファイルを編集する。

% vim fruits.hs
myFavoriteFruit = "banana"

対話環境で値を表示する。ファイルを再読み込みするためには、コマンド:reloadを使う。

*Main> :reload
*Main> myFavoriteFruit
"banana"

関数を定義する

関数doubleを定義してみる。

% vim functions.hs
double x = x * 2

関数を使う

関数doubleを使ってみる。

% stack ghci
Prelude> :load functions.hs
*Main> double 8
16

関数リテラル

関数リテラルを使うと、関数に名前をつけずに直接、作ることができる。関数定義は、関数リテラルによって作った関数で、変数を束縛していると考えることができる。

*Main> (\x -> x * 2) 8
16

演算子と関数

演算子と関数とには、見た目のちがいだけしかない。演算子には、記号列を使う。また、中置記法となる。演算子と関数とは、``や()でたがいに変換できる。

*Main> 3 + 5
8
*Main> (+) 3 5
8
*Main> div 128 5
25
*Main> 128 `div` 5
25

型を宣言する

変数luckyを定義する。Integer型として宣言することにしよう。

% vim functions.hs
lucky :: Integer
lucky = 7

関数doubleにも型宣言をつけてみよう。

double :: Integer -> Integer
double x = x * 2

対話環境に読みこもう。

*Main> :reload
*Main> lucky
7
*Main> :type lucky
lucky :: Integer
*Main> double lucky
14

数値型の例

数値をあらわす型には、数値の種類に応じて、さまざまなものがある。たとえば、整数値をあらわす型Integerや、浮動小数点数をあらわす型Doubleなどがある。

型注釈

Haskellには、さまざまな型の値に解釈されることのある、多相的な式を書くことができる。このような式では、型注釈をつけないと、ひとつの型の値として評価できないことがある。たとえば、数値リテラルなども、そのような式のひとつである。対話環境で試してみよう。

*Main> 123 :: Integer
123
*Main> 123 :: Double
123.0

Bool値

Bool値とは、偽(そうでない)と真(そうである)とをあらわす、ふたつの値のこと。

*Main> False
False
*Main> True
True

Maybe値

Maybe値とは、値がないかもしれない値。「失敗するかもしれない」関数の返り値として使われる。値があるときは「Just 値」のように表記/表示され、値がないときには「Nothing」のように表記/表示される。

*Main> Just 8
Just 8
*Main> Nothing
Nothing

タプル

タプルについても、試してみよう。たとえば、つぎのようなタプルを定義する。

% vim functions.hs
taro :: (String, Integer)
taro = ("Taro Yamada", 35)

対話環境で、みてみよう。

% stack ghci
Prelude> :load functions.hs
*Main> taro
("Taro Yamada",35)
*Main> :type taro
(String,Integer)

パターンマッチ

パターンマッチは、Haskellでは本質的な機能のひとつ。分岐と、複合的な値からの値のとりだしとが、パターンマッチによって実現される。たとえば、失敗するかもしれない計算の結果を受けとって、値をかえす関数を、みてみよう。

% vim functions.hs
helloTo :: Maybe String -> String
helloTo (Just n) = "Hello, " ++ n ++ "!"
helloTo Nothing = "Hello, customer!"

タプルの、それぞれの要素の取り出しも、パターンマッチでできる。

human :: (String, Integer) -> String
human (n, a) = n ++ "(" ++ show a ++ ")"

関数showは数値を文字列に変換している。リテラルもパターンとして使える。

safeRecip :: Double -> Maybe Double
safeRecip 0 = Nothing
safeRecip x = Just (1 / x)

それぞれの関数を試してみよう。

% stack ghci
Prelude> :load functions.hs
*Main> helloTo (Just "Yoshikuni")
"Hello, Yoshikuni!"
*Main> helloTo Nothing
"Hello, customer!"
*Main> human ("Taro Yamada", 32)
"Taro Yamada(32)"
*Main> safeRecip 8
Just 0.125
*Main> safeRecip 0
Nothing

ワイルドカード

値に束縛された変数を使わないときには、ワイルドカードが使える。ワイルドカードはアンダーバーで示される。

% vim functions.hs
isNothing Nothing = True
isNothing (Just _) = False

試してみよう。

*Main> :reload
*Main> isNothing Nothing
True
*Main> isNothing (Just 8)
False

@パターン

@パターンを使うと、変数束縛したうえで、さらに細かいパターンにマッチさせることができる。

% vim functions.hs
atPattern :: Maybe Integer -> String
atPattern jx@(Just x) = show jx ++ ": " ++ show x
atPattern Nothing = ""

試してみよう。

*Main> :reload
*Main> atPattern (Just 8)
"Just 8: 8"
*Main> atPattern Nothing
""

ガード構文

Bool値に対するパターンマッチは頻出なので、特別な構文がある。安全に平方根をもとめる関数を定義してみる。

% vim functions.hs
safeSqrt :: Double -> Maybe Double
safeSqrt x
        | x < 0 = Nothing
        | otherwise = Just (sqrt x)

試してみよう。

% stack ghci
Prelude> :load functions.hs
*Main> safeSqrt 9
Just 3.0
*Main> safeSqrt 2
Just 1.142135623730951
*Main> safeSqrt (- 5)
Nothing

if式

おなじことをif式で書いてみる。

% vim functions.hs
safeSqrt' :: Double -> Double
safeSqrt' x = if x < 0 then Nothing else Just (sqrt x)

うえとおなじようにして、試してみよう。

case式

case式を使うと、関数定義ではなく式のなかでパターンマッチができる。

% vim functions.hs
yesNo :: Char -> Maybe Bool
yesNo c = case c of
        'n' -> Just False
        'y' -> Just True
        _ -> Nothing

試してみよう。

*Main> :reload
*Main> yesNo 'y'
Just True
*Main> yesNo 'c'
Nothing

多相

多相性のある関数を定義してみよう。ふたつめの引数を無視する関数は、つぎのようになる。

% vim functions.hs
ignoreSecond :: a -> b -> a
ignoreSecond x y = x

第1引数、第2引数ともに、どのような型であっても、この関数は実行できる。また、第1引数と返り値の型は、おなじでなければならないので、おなじ型変数aで表現されている。試してみよう。

% stack ghci
Prelude> :load functions.hs
*Main> ignoreSecond 8 "hello"
8
*Main> ignoreSecond False 123.45
False
*Main> :type ignoreSecond
ignoreSecond :: a -> b -> a

型シノニム

予約語typeによる構文を使って、型の別名を定義することができる。

% vim functions.hs
type Human = (String, Integer)

showHuman :: Human -> String
showHuman (n, a) = n ++ "(" ++ show a ++ ")"

試してみよう。

% stack ghci
Prelude> :load functions.hs
*Main> showHuman ("Taro", 39)
"Taro(39)"

型引数

型シノニムの定義には、型引数を使うことができる。

% vim functions.hs
type Check a = (Bool, a)

tasks :: [Chack String]
tasks = [(False, "Shopping"), (True, "Walking")]

対話環境で見てみる。

% stack ghci
Prelude> :load functions.hs
*Main> tasks
[(False,"Shopping"),(True,"Walking")]
*Main> :type tasks
tasks :: [Check String]

モジュールの導入

ソースファイルのなかでモジュールを導入するには、予約語importを使う。

% vim useMaybe.hs
import Data.Maybe (fromMaybe)

maybe0 :: Maybe Integer -> Integer
maybe0 = fromMaybe 0

試してみよう。

% stack ghci
Prelude> :load useMaybe.hs
*Main> maybe0 (Just 8)
8
*Main> maybe0 Nothing
0

コメント

--から行末までと、{-と-}とで、かこまれた部分とは、コメントとなる。コメントはコードに影響をあためない。

% vim functions.hs
some :: Integer
some = 8        -- some is 8

hoge :: Integer -> Integer
hoge x = x * 3
{-
hoge 3 = 9
hoge 10 = 30
...
-}

試してみよう。

% stack ghci
Prelude> :load functions.hs
*Main> some
8
*Main> hoge 5
15
12
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
6