「Haskell入門ハンズオン #2」の当日用の資料(2)

「Hskell入門ハンズオン #2」の当日用の資料(2)
========================================

関数適用演算子と関数合成演算子
------------------------

### 関数適用演算子

関数適用演算子(\\$)を紹介する。ここで、逆数をもとめる関数recipについて考える。

```hs
> recip 8
0.125
```

これは、「関数recipを数値8に適用した」ということだ。演算子(\\$)は、第1引数の関数を第2引数の値に適用する。

```hs
> recip $ 8
0.125
```

「なぜ、こんな演算子が定義されているのだろう?」「必要ないのでは?」関数適用演算子(\\$)のレーゾンデートル(存在意義)は?つぎの例をみてみよう。

```hs
> logBase 2 (recip (3 + 5))
-3.0
```

3 + 5の逆数が2の何等であるかをもとめている。このくらいならいいが、「逆数の対数の絶対値の...」のように、長々と空いていくことを考えよう。

```hs
abs (logBase 2 (recip (...)))
```

最後に閉じ括弧が...))))))))のように続くことになる。つぎの例をみてみよう。

```hs
> logBase 2 $ recip $ 3 + 5
-3.0
```

関数適用演算子(\\$)なら...))))))))のようにはならない。関数の適用の連鎖が長々と続いたりする。

```hs
abs $ logBase 2 $ recip $ ...
```

丸括弧が入れ子になるよりスマートに書ける。

### 関数合成演算子

関数合成演算子を紹介する。小文字にして文字コードをもとめる関数の例でみていこう。

```hs
> :module Data.Char
> fun c = ord (toLower c)
> fun 'Y'
121
```

この関数funは関数ordとtoLowerとを、くっつけたものだ。このように「くっつける」ことを「関数合成」と呼ぶ。関数合成演算子(.)を使った関数fun2は、つぎのようになる。

```hs
> fun2 = ord . toLower
> fun2 'Y'
121
```

関数funよりもfun2のほうが、「小文字化して、文字コードをもとめる」という意味を、直接的に表現できている。

### 関数適用演算子を関数合成演算子に置き換える

つぎのように関数適用を2回以上使う例をみる。

```hs
> abs $ logBase 2 $ recip 8
3.0
```

これを、つぎのように書き換えることができる。

```hs
> abs . logBase 2 $ recip 8
3.0
```

結合力が演算子(.)のほうが(\\$)よりも強いことに注意する。また、演算子(\\$)は右結合だ。よって、それぞれについて、丸括弧を明示すると、つぎのようになる。

```hs
abs $ (logBase 2 $ recip 8)
(abs . logBase 2) $ recip 8
```

「8の逆数の対数をもとめて、絶対値をもとめる」のと、「8の逆数に、絶対値関数と対数関数を合成したものを適用する」のふたつは、おなじ意味になる。

演算子(\\$)が連続したとき、1番右の(\\$)以外は、すべて、演算子(.)に置き換えられる。逆に、演算子(\\$)が一番右にあるとき、その左の演算子(.)の連続は、すべて、演算子(\\$)に置き換えて、解釈できる。理屈を理解しなくても、つぎのような置き換えができることだけ、覚えておこう。

```hs
f $ g $ h $ i $ j $ ... $ z x
f . g . h . i . j . ... $ z x
```

個人的には前者のような、かたちがあらわれたら、後者のように書き換えることにしている。「見ため」がきれいだからだ。

いくつかの関数を学ぶ(1)
-------------------

Haskellで、はじめから定義されている関数のうちのいくつかを紹介する。ここでは、つぎの7個を学ぶ。

* 関数map, filter, take, drop
* 関数replicate, reverse
* 演算子(++)

### 関数map

関数mapは、リストのすべてについて、おなじ変換をする。「変換」をあらわす関数を第1引数にする。

```hs
> double x = x * 2
> map double [1, 2, 3, 4, 5]
[2,4,6,8,10]
```

第1引数に「引数を2倍にする関数」をとり、リストのすべての要素に、それを適用している。

### 関数リテラル

ところで、関数mapの第1引数とするために、つぎのような関数doubleを定義した。

```hs
double x = x * 2
```

この関数doubleは、これ以降、使う予定はない。それなのに、いちいち名前をつけるのは、めんどうだ。このようなとき関数リテラル(無名関数)を使う。

```hs
> map (\x -> x * 2) [1, 2, 3, 4, 5]
[2,4,6,8,10]
```

関数リテラルの記法は、つぎのようになる。

    \[仮引数1] [仮引数2] ... -> [式]

### 関数filter

リストから条件を満たす値だけ取り出す関数だ。リストから奇数だけを取り出してみる。

```hs
> filter odd [1, 2, 3, 4, 5]
[1,3,5]
```

第1引数である関数oddによって、奇数かどうかを判定する。

### 関数replicate

おなじ値をくりかえしたリストを作成する。

```hs
> replicate 10 123
[123,123,123,123,123,123,123,123,123,123]
```

整数値123を10回くりかえしたリストだ。

### 関数take, drop

リストの前のほうのいくつかを取り出す。

```hs
> take 3 [1, 2, 3, 4, 5]
[1,2,3]
```

リストの前のほうのいくつかを落とす。

```hs
> drop 3 [1, 2, 3, 4, 5]
[4,5]
```

それぞれ、前のほうの3つの要素、前のほうの3つ以外の要素が、取り出されている。

### 関数reverse

リストを逆順にする関数だ。

```hs
> reverse [1, 2, 3, 4, 5]
[5,4,3,2,1]
```

リストが逆順になった。

### 演算子(++)

ふたつのリストを結合する。

```hs
> [1, 2, 3] ++ [4, 5]
[1,2,3,4,5]
```

リストが結合された。Haskellでのデフォルトの文字列は文字のリストなので、文字列は演算子(++)で結合できる。

```hs
> "Hello" ++ "World"
"HelloWorld"
```

### まとめ

ここまで紹介してきた関数(mapから演算子(++)まで)は、どれも自分で定義できる。定義は再帰的になる。時間の関係で、ここでは説明しない。これらの関数を使いこなせるようになろう。ここで学んだ関数は、つぎの7個である。

* 関数map, filter, replicate
* 関数take, drop
* 関数reverse, 演算子(++)

いくつかの関数を学ぶ(2)
-------------------

さらに、つぎの4個の関数を学ぶ。

* 関数randoms, mkStdGen
* 関数unlines
* 関数read

### 関数randoms, mkStdGen

乱数のリストを生成する関数を紹介する。関数randomsなどは、現在では、標準的なパッケージには含まれていないので、いちど対話環境をぬけて、randomsパッケージを導入しながら対話環境を立ち上げる。

    > :quit
    % stack ghci --package random

モジュールSystem.Randomを導入する。

```hs
> :module System.Random
```

関数randomsは「乱数の種」を引数として、ランダムな値を要素とする無限リストをかえす。「乱数の種」は関数mkStdGenで整数から作れる。

```hs
> take 10 . randoms $ mkStdGen 8
[-398575370259562870,-6370604356117182359,
8399777519602674086,...
```

### 関数unlines

文字列を要素とするリストを、行の集わりと解釈して、それぞれの行のおわりに改行を追加した、ひとつの文字列としてかえす関数。

```hs
> unlines ["hello", "world"]
"hello\nworld\n"
```

helloやworldの、それぞれのうしろに\n(改行)が追加される。そのうえで、結合され、ひとつの文字列になる。

### 関数read

文字列を適切な値に変換する。

```hs
> read "12345" + 54321
66666
```

文字列"12345"が整数値12345に変換された。

入出力を学ぶ
----------

### 関数

いろいろな関数をみてきた。関数に引数をあたえたものが値に評価される。たとえば、つぎのような計算を考える。

```hs
> 3 + 2 * 5
13
```

関数(演算子)である(+)や(*)に対して、整数値3, 2, 5をあたえることで、式3 + 2 * 5が作られ、それが値13に評価される。関数は引数をとり、それによって作られた式が評価される。関数にできることは、それだけだ。

### 動作

式を値に評価すること。たしかに、それができるのは、すばらしいこと。しかし、僕らがプログラミングでやりたいことは、それだけじゃない。ウェブサーバを作るにも、ゲームを作るにも、何らかの「動作」が必要になる。「純粋な関数型」のわくぐみで「動作」をあつかいたい。どう考えるか?基本になる「動作」を、関数によって組み合わせればいい。関数putStrLnは、文字列を表示する。

```hs
> putStrLn "hello"
hello
> putStrLn "world"
world
```

putStrLn "hello"は「helloと表示する動作」に評価される。対話環境は、評価の結果が「動作」になったとき、その動作を「実行」する。これらの動作を、「XのつぎにYを実行する」のように組み合わせる。

```hs
> putStrLn "hello" >> putStrLn "world"
hello
world
```

「基本になる動作」を関数によって組み合わせて、「複雑な動作」を組み立てていく。ファイルaction.hsに、つぎのように定義する。

```hs:action.hs
hello = putStrLn "hello" >> putStrLn "world"
```

対話環境に読み込み、試してみる。

```hs
> :load action.hs
> hello
hello
world
```

動作を「関数」で組み合わせていくことで、複雑な動作を組み立てていくことができる。演算子(>>)で動作を組み合わせて、より複雑な動作を組み立てていく。もちろん、それは「いい考え」だ。しかし、たとえばレシピを考えてみよう。

    鍋を火にかける
    沸騰したらパスタを入れる
    パスタが、やわらかくなったらザルにあげる

このように、順番に書かれた内容は、順番に実行されることが期待される。Haskellでは、明示的に演算子(>>)を書かなくてすむ「do記法」という書きかたが用意されている。do記法でaction.hsの関数helloを書き換えてみよう。

```hs:action.hs
hello = do
        putStrLn "hello"
        putStrLn "world"
```

このように明示的な演算子(>>)を書くかわりに、動作を並べて書くことができる。注意する点は、列挙される動作は、予約語doのある行よりも深くインデントし、それぞれの「列挙される動作」どうしのインデントは「そろえる」ということだ。どうだろうか。「ふつうの言語」の書きかたに近くなったのではないだろうか。

### 入力

関数putStrLnによって作られる動作は「出力」だ。動作には「出力」だけでなく「入力」もある。たとえば、ユーザの打ち込んだ文字列を取り込みたい。そんなときは、「値をかえす動作」を使う。

```hs
> getLine
(helloと入力する)hello
"hello"
```

動作getLineはユーザからの入力を待ち、打ち込まれた文字列を動作からかえる値とする。対話環境は式が「値をかえす動作」に評価されたとき、その動作を実行し、さらに、かえされた値を表示する。かえされた値を使うことを考えよう。名前を入力すると、「Hello, 誰々!」のように、あいさつしてくれる動作をつくる。ファイルaction.hsに、つぎの関数を定義する。

```hs:action.hs
greeting = do
        name <- getLine
        putStrLn $ "Hello, " ++ name ++ "!"
```

試してみる。

```hs
> :reload
> greeting
(自分の名前を入力)Yoshikuni
Hello, Yoshikuni!
```

つぎのように「<-」を使うことで、動作からかえされた値を変数に束縛できる。

```hs
var <- action
```

この変数varは、このあとに列挙される動作のなかで、使うことができる。

打ち込まれた文字列を数値に変換し、それを摂氏温度として、絶対温度に変換された値をかえす動作を考える。ファイルaction.hsに、つぎの関数を定義する。

```hs:action.hs
getAbsolute = do
        c <- getLine
        return $ read c + 273
```

試してみる。

```hs
> :reload
> getAbsolute
(25と入力)25
298
```

動作getAbsoluteの最後の行では、returnという関数が使われている。関数returnは「何もせずに」引数の値をかえす動作を作る。この動作を最後に置くことで、動作getAbsoluteで、絶対温度の値をかえすことができる。

入出力に関する関数、動作
-------------------

いくつかの入出力に関する関数、動作を学ぶ。ここで学ぶ関数、動作は、つぎの6個だ。

* 関数putStr, print, 動作getChar
* 動作getArgs
* 関数hGetBuffering, hSetBuffering

### 関数putStr

すでに関数putStrLnをみた。

```hs
> putStrLn "hello"
hello
```

関数putStrLnは引数である文字列を、標準出力に出力したあと、改行を出力する。関数putStrは改行を出力しない。

```hs
> putStr "hello"
hello>
```

改行が出力されないので、プロンプトが出力のあとに続く。

### 関数print

関数printは文字列ではなく「値」を表示する。

```hs
> print 123
123
```

値を文字列化する関数showがある。

```hs
> show 123
"123"
```

関数printは関数putStrLnとshowを結合したものだ。

```hs
> (putStrLn . show) 123
123
```

実は、対話環境は、評価した結果が動作でないとき、結果の値を関数printに引数としてあたえ、作られた「動作」を実行していた。よって、対話環境では、つぎのふたつの結果がおなじになる。

```hs
123
print 123
```

### 関数getChar

ユーザの打ち込んだ文字列を行単位で取得する関数getLineはすでにみた。ユーザの打ち込んだ文字を、ひとつだけ取得する関数getCharをみてみよう。

```hs
> getChar
(cと入力する)c'c'
```

動作getCharは入力された文字をかえす。かえされた文字は対話環境によって表示される。

### 動作getArgs

ターミナルから呼び出すアプリケーションを作っていると、コマンダライン引数を取得したくなる。動作getArgsを使う。ファイルaction.hsに、定義を追加する。

```hs:action.hs
printArgs = do
        as <- getArgs
        print as
```

関数getArgsは、モジュールSystem.Environmentから公開されている。ファイルの先頭に、つぎのように追加する。

```hs:action.hs
import System.Environment
```

モジュールを導入するには予約語importを使う。対話環境で試してみる。

```hs
> printArgs
[]
```

対話環境なので、コマンドライン引数は指定されていない。動作getArgsのかえすリストは空リストになる。対話環境のなかでコマンドライン引数を指定するには、コマンド:runを使う。

```hs
> :run printArgs hello world
["hello","world"]
```

### バッファリング

バッファリングというものがある。バッファリングのモードを変えることで、一文字ずつ入力を行うのか、一行ずつ入力を行うのか、を変えることができる。現在のバッファリングのモードを確認する。

```hs
> :module System.IO
> hGetBuffering stdin
NoBuffering
```

現在のバッファリングのモードはNoBufferingである。つまり、入力は一文字ずつ処理される。

```hs
> getChar
(cと入力)c'c'
```

バッファリングのモードを変えるには、関数hSetBufferingを使う。

```hs
> hSetBuffering stdin LineBuffering
> hGetBuffering stdin
LineBuffering
> getChar
(c123と入力し改行を入力)c'c'
> 123
123
```

改行を入力するまで、文字'c'は読み込まれない。1文字読み込んだあとの文字列"123"は、対話環境への「つぎの入力」とされる。

モジュールと実行可能ファイル
-----------------------

プログラムを組みうえで、モジュール分割という考えかたは重要だ。Haskellでは1モジュール、1ファイルとなる。モジュールの作りかたをみる。また、単独で実行可能なファイルの作りかたをみる。

### モジュール

適当なモジュールを作ってみる。つぎのようなファイルSome.hsを作る。

```hs:Some.hs
module Some where

fun x = x * x
```

対話環境に読み込む。

```hs
> :load Some.hs
*Some> fun 8
64
```

自分で作ったモジュールを、定義ファイルのなかで、使うことができる。ファイルother.hsを作成する。

```hs:other.hs
import Some

f = fun . fun
```

対話環境で試してみよう。

```hs
> :load other.hs
> f 3
81
```

プロンプトに、そのモジュールの名前が表示される。

### 実行可能ファイル

実行可能ファイルを作るには、動作で「特別な変数main」を束縛する。つぎのようなファイルhello.hsを作る。

```hs:hello.hs
main = putStrLn "Hello, world!"
```

つぎのようなコマンドを実行する。

    % stack ghc -- hello.hs -o hello

すると、実行可能ファイルhelloが作成される。

    % ./hello
    Hello, world!

[資料(3)へ続く](https://qiita.com/YoshikuniJujo/items/9b4aeab57e8f0175f7b8)