関数型言語 Haskell

作成日 : 2006-10-01
最終更新日 :

Haskellの本

関数型言語 Haskell

青木峰郎氏の「ふつうのプログラマのための Haskell 入門」を借りた。 また、向井淳氏の「入門 Haskell」も借りた。 どういうわけか、2つとも同じ図書館にあったのである。 それらをもとに、少しずつ Haskell のことを書く。

インストール

私は、Vine Linux を使っている。 Vine Linux 純正の配布パッケージはない。 そこで、インストールは、 http://haskell.org/ghc/download.html から、Current Stable Release (私がインストールした時は 6.4.2) の Linux(x86) の RedHat Linux/x86 9.0 RPMs (base RPM) を使った。 普通の rpm と同様にインストールして、順調に動作している。

その後、わけあって Windows XP を使っている。こちらでも順調に動作している。

現在では、上記 GHC を素でインストールするより、Haskell Platform (http://www.haskell.org/platform/) をインストールすることが勧められているので、私もそれに従う。 インストーラの容量は100MB 強である。 (2013-06-01)。

さらにそのあとは、64 bit 版を Windows 版 8.1 にインストールした(2014-12-14)。

さらにそのあとは、Windows 11 に 64 bit 版をインストールした。なお、このとき、 https://www.haskell.org/ghcup/ から ghcup の PowerShell session でインストールしようとしたが、 msys2 のインストールで失敗した。そこで、下記のサイト
WindowsにGhcup(Haskell開発ツールチェイン)をインスコ (scrapbox.io) に従って、msys2 のみを最初にインストールした。 msys2 は無事インストールでき、また msys2 の更新も問題なく完了した。 それから ghcup を行なった。ところが、curl で失敗してしまう。

curl: (60) SSL certificate problem: self-signed certificate in certificate chain
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
Press any key to exit

どうやらこれはセキュリティソフトを起動していたためとわかった。そこで一次的にセキュリティソフトの保護機能を切った。 そして再度インストールを試みると、MinGW 64 のコンソールが別途開く。このコンソールを見る限り、curl が無事動いたようである。

ところが今度は次のエラーが表示されてしまった。

curl: (6) Could not resolve host: downloads.haskell.org
[ Error ] [GHCup-12971] Both installation and setting the tool failed. Install error was: Download failed: Process "curl" with arguments ["-fL",
[ ...   ]
                        "-o",
[ ...   ]
                        "C:/ghcup\\cache\\cabal-install-3.6.2.0-x86_64-windows.zip.tmp",
[ ...   ]
                        "https://downloads.haskell.org/~cabal/cabal-install-3.6.2.0/cabal-install-3.6.2.0-x86_64-windows.zip"] failed with exit code 6.
[ ...   ] Set error was: The version 3.6.2.0 of the tool cabal is not installed.
[ Error ] Also check the logs in C:/ghcup\logs
"ghcup --metadata-fetching-mode=Strict --cache install cabal recommended" failed!
cat: 'https'$'\357\200\272''/www.haskell.org/ghcup/sh/bootstrap-haskell': No such file or directory
Press any key to exit
Press any key to exit
	

仕方がない。もう一度やってみてダメだったらあきらめよう。ところが、 今度はうまくいった。不思議だ。

	===============================================================================

In order to run ghc and cabal, you need to adjust your PATH variable.
To do so, you may want to run 'source /c/ghcup/env' in your current terminal
session as well as your shell configuration (e.g. ~/.bashrc).

===============================================================================

All done!

In a new powershell or cmd.exe session, now you can...

Start a simple repl via:
  ghci

Start a new haskell project in the current directory via:
  cabal init --interactive

Install other GHC versions and tools via:
  ghcup list
  ghcup install <tool> <version>

To install system libraries and update msys2/mingw64,
open the "Mingw haskell shell"
and the "Mingw package management docs"
desktop shortcuts.

If you are new to Haskell, check out https://www.haskell.org/ghcup/steps/
Press any key to exit

参考
ghcup(qiita)

練習問題

頭から練習問題を解いていくのが苦手なので、 気に入った問題から行うことにする。まずは、 向井氏の「入門 Haskell 」から。

86 ページの格子点列挙問題を考えてみよう。 2次元の格子点列挙問題はわかった。 では次のページの 87 ページにある、 任意の n 次元の格子点に対応する列挙はどうするか。

2次元の場合はタプルで対を作っていたが、 n 次元への拡張では、タプルでは難しい。 リストにすべきだろう、そこまではわかった。

それから先は苦労した。 ヒントに従って、再帰を使ってコードを書いてみた。

lattice d n = [i : lattice (d-1) (n-i) | i <- [0..n]]
lattice 1 n = [n]
all_lattices d = concat [ lattice d i | i <- [0..] ]

結果は失敗。エラーメッセージは次の通り

lattice.hs:1:15:
    Occurs check: cannot construct the infinite type: t = [t]
      Expected type: t
      Inferred type: [t]
    In the expression: i : (lattice (d - 1) (n - i))
    In the expression: [i : (lattice (d - 1) (n - i)) | i <- [0 .. n]]
Failed, modules loaded: none.

どうやら言いたいのは、こういうことのようだ。

Occurs check: 有限型: t = [t] を構成できない 期待される型: t 推論される型: [t]

といっても、わけがわからない。悪戦苦闘して、 結局自力でわからなかったので、Web で確認した。 あ!なるほど。次のように書けばよかった。

lattice 1 n = [[n]]
lattice d n = concat [map (i:) (lattice (d-1) (n-i)) | i <- [0..n]]
all_lattices d = concat [ lattice d i | i <- [0..] ]

まず、順番が違う。最初に lattice 1 n の定義が必要だ。 そしてその定義では [n] ではなく、[[n]] が必要。 初版本の記述は誤植。

次に、lattice d n の定義では、 数字を(再帰した)リストに直接 : によって先頭に挿入しようとバカなことを考えていた。 当然、map が必要だ。 concat は、型を合わせるためである。

実習:分散分析

では、今までに習ったことをもとにして、どこまで記述できるのかを確かめよう。 m 行 n 列の行列 a(m, n) に対して、次を求める。

仮定として、行列はリストのリストで構成されているとする。 全要素の平均は、次の通りでいいだろう。

	average xs = sum xs / fromIntegral (length xs)
	matrixAverage xs = average (concat xs)

行列はリストの入れ子になっているので、平均を求めるためにリストの入れ子をはずす。 このときの関数が concat であるのに気付かなかった。 最初、ruby からの類推で flatten という名称かと思ったのだが、 そのような名前の関数は Haskell にない。 探し方に困ったが、関数定義が [[a]] -> [a] であるはずだから、 検索のキーワードに [[ と入れてみた。そこで、見事 concat だとわかった。

次は、1行(1列)の要素の和はどうだろう。

	rowSum xs = map sum xs
	columnSum xs = map sum (transpose xs)

行の和は、map を適用すればよい。列の和は、transpose にまかせた。 計算効率のことは考えていない。 1行(1列)の要素の平均と、全要素の平均の差はどうだろう。


	rowResidue xs = map (\x -> x - matrixAverage xs) (map average xs)
	columnResidue xs = transpose xs

これでよいはずだ。そのほか、ついでに二つのリストをベクトルとみなしたときの 内積を定義しよう。


	innerProduct xs ys = sum (zipWith (*) xs ys)

ここまでやっておけば大丈夫だろう。

エラトステネスのふるいとメビウス関数

エラトステネスのふるいとは、素数をもとめるアルゴリズムとしてよく知られている。 Haskell では次の実装が知られている。


primes = sieve [2..] 
sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]

これを応用して、メビウス関数をすっきりと求める方法が Haskell にはあるのではないかと思い、 考えたり調べたりしたが、なかなかみつからない。

数学パズルの問題

「プログラマ脳を鍛える数学パズル」という書籍がある。この Q01 は、 十進数、二進数、八進数のいずれで表現しても回文数となる数のうち、 十進数の 10 以上で最小の値を求めよ、というものである。ただし回文数とは、 逆から数字を読んでも同じ数になる数のことをいう。

いきなり手ごわい問題である。考え方としては、二進数で回文数になることから、 二進数の末尾は 0 ではなく 1 である。これは、末尾が 0 だとすると、 先頭も 0 となり、そのような数字の並びは普通数と認めないからである。 ということは、求める数は十進数では基数であることが必要条件であるから、 10 以上であるということと合わせて、[11 13 .. ]というリストを作っておいて、 二進数表示、八進数表示、十進数表示のすべてが回文数になっているものを確かめればよいことになる。

リンク

まりんきょ学問所関数型言語手習い > 関数型言語 Haskell


MARUYAMA Satosi