ラベル Haskell の投稿を表示しています。 すべての投稿を表示
ラベル Haskell の投稿を表示しています。 すべての投稿を表示

すごいHaskell 読書メモ #3

2015年10月22日木曜日

前回: すごいHaskell 読書メモ #2
前回はデータ構造の序盤ということでリストの話と内包表記の話などでした。

タプル

今回はタプルです。タプル自体は他の言語でも馴染み深いものです。

メモ

  • タプルのリストを作る際に異なるシグニチャのものを混ぜてはいけないというのが新鮮に感じた
    • 前回やったリストのポリシーを考えると「リストは特定の型の要素の列」ということなのだから当然だった
  • 2要素のタプルはペアとも呼ばれ、3要素のタプルはトリプルとも呼ばれる
    • ペアに対して先の要素と後の要素を取り出すfstsndという関数は頻出らしい
      • 名前的には覚えやすい
    • トリプルから同様に値を取り出すものについてはちょっと後で出てくるとのこと。
  • リストはサイズの違うもの同士で比較できるのに対し、タプルは同サイズ同士でしか比較できない
  • zip便利だなぁ
    • ふたつのリストから組にして値を取り出してペアのリストにするというオペレーション結構よく使いそう

演習問題

直角三角形の問題は、
ghci> [(x,y, sqrt(x^2 + y^2)) | x <- [1..10], y <- [1..10], sqrt(x * x + y * y) + x + y == 24]
[(6.0,8.0,10.0),(8.0,6.0,10.0)]
と強引に解きました。解法を見たら泣けるほどきれいにフィルタリングで条件を詰めていっていてそれっぽかった。
c <- [1..10], b <- [1..c]などの値の持つ性質を反映したリストを作るというのは結構大事なポイントになるんだろうな。
多少手が動くようになってきたところで今回は終わり。次回は第2章「型を信じろ!」の話です。

すごいHaskell 読書メモ #2

2015年10月7日水曜日

前回: すごいHaskell 読書メモ #1
前回は関数の記述と適用の話だった。if式にはelse節が必須など、いくつかのルールを学んだ。
今回からはデータ構造の話、今回はリスト。

リストの基本操作と性質

まずはリスト。定義の仕方や扱い方はざっくりと他の言語と変わらないように見える。GHCi内での定義にはletを使うとひっそりtipsが書かれてる。
リストの連結は++でおこなう。文字列は文字のリストとして表現されるので、リスト関数は文字列に対して適用できる。
リストの末尾に連結をおこなうのは何度もスキャンが必要となるため遅い。リストの先頭への追加はとても速い(スキャンしないから)。これをおこなう演算子が:cons演算子とも呼ばれる。
ここの例で'h'など文字(Cでいうところのchar相当)指定が出てる。前回の中身でアポストロフィは名前の一部に使えるというのがあったけど、この感じだと先頭には使えない(letter表現とかぶるため)のだろう。
cons演算子(:)で第一引数になるのは連結対象リストの単一要素。++演算子では両方ともリストを渡す。
Haskellでは[1,2,3]は単に1:2:3:[]の糖衣構文と。なるほど。
リストの要素へのアクセスは!!でおこなう。0-origin。リストの端を超えた場所にアクセスすると例外が投げられる。リストの中に更にリストを入れられるけれど、違う型はダメ。
リストの比較は辞書順でおこなわれる。最初に見つかった異なる要素の順序が大小を決定する。空でないリストは常に空リストよりも大きいとみなされる。なので、リストAがリストBのスーパーセットである場合はリストAのほうが大きいとみなされる。ふむ。
その他のリスト操作としてhead/tail/last/initの紹介。結構頻繁に使いそう。
空のリストに対してこれらの操作をすると例外を吐く。length関数で要素数を調べたり、null関数で空か否かを調べたりして空リストへの操作が発生しないように処理すると。
take関数は要素数を指定して取り出す。0を指定して空リスト取り出しができる。試した感じ、take 0 []は問題ない。リスト要素数より多い要素を取り出そうとするとリスト全体が返される。だからtake 3 []は空リストを返すだけで特に問題ない。
maximumminimum関数は想像通りの挙動。elemは要素とリストを受け取って、指定要素がリストに含まれるかを返すもの。中置関数として使うことが多いという説明がされてる。
レンジ指定はまあまあ賢いよという話。[1..20]は自然数を刻んでくれるし、[a..z]は小文字アルファベットを列挙してくれる。
しかし[1.1..20.0]を試してみると
[1.1,2.1,3.1,4.1,5.1,6.1,7.1,8.1,9.1,10.1,11.1,12.1,13.1,14.1,15.1,16.1,17.1,18.1,19.1,20.1]
となったので、まあこんなもんかなと。
2つ目の要素を指定するとその差分を刻みとしてほどよくやってくれると。試しに[1,3..]とすると噂の無限リストがさっくり作れた。割と便利さを感じる。と思ったらすぐ後にそのまま無限リストの話が出てきた。
cycle関数は指定リストを無限にリピートする。おもしろい。repeatは単一要素を受け取ってそれをひたすら繰り返す(yesコマンドぽい)。replicateは指定要素からなる指定長のリストを作るもの。結果だけだとreplicate 3 10take 3(repeat 10)は同じになるけど、内部の処理的にはどっちのほうが効率的なのだろう、というのが気になったりした。
続けてリスト内包表記の話。Pythonなどでお馴染みという感じだけど、あるリストから要素列を取り出して指定の名前に束縛し、加工方法を記載する出力パートに投げ込んで結果リストを作っておしまい。
要素列の取り出しと束縛をおこなうパートにカンマで続けて条件(述語とも呼ぶ)をつけ、元リストからの取り出しをフィルタできると。条件はカンマで区切っていくつも書けるので、複数条件指定のためにわざわざリスト作成を何重にもおこなう必要はない。
複数のリストからの取り出しも構文的には条件と同様なんだけど、利用時をイメージするとイマイチ直感的ではなかった。取り出した要素の全組み合わせが結果リストに反映される(5要素のxと3要素のyなどを取り出すと15要素の結果リストができる)。慣れると便利な局面が多そう。
lengthっぽい関数を独自定義するくだりはゴリ押しすぎて面白かった。まさかの全要素を1に射影してsum*1
リストから取り出したけど使わない値を変数名_に突っ込んで捨てるのはお馴染みのイディオム。
試してみると複数のリストからの取り出しで全部同じ変数へ突っ込むこともできるんだけど、その場合実際に取り出せるのは最後に書いたものらしい。
ghci> [x | x <- [1,2,3], x <- [10,100]]
[10,100,10,100,10,100]
ふむ。どっかに仕様書いてあるだろうからそのうち調べそうなメモ。
リストを含むリストの操作で例示されている[ [ x | x <- xs, even x ] | xs <- xxs ]は慣れるとサクサク読み書きできそうだけど、あまり複雑になりすぎると脳スタックがあふれそうな気もした。外枠から把握していくとルール付ければ破綻はしないか。
と思ったら、入れ子の内包表記は複数行で書いたほうが読みやすいよと注記されていた。誰かのコードを読むなかで慣れよう。
[*1] C#+LINQやRxの文脈でも似たことをやるコードは見かけますが、たとえば極単純な配列長チェックに使う場合は確実に無駄ですね。特定条件でフィルタした結果の件数取得などでは合理的な可能性あります。

おしまい

リストについてはここまで。写経しつつメモを書きながら読み進めたところ、分量的にはあまり進まなかった。各関数の挙動を実際に試しつつなので、まあこんなものかなとも思う。
確実に「おっけー!」という部分がまだほとんどないので、出てきたものを片っ端からメモ取る状態になってるのが辛いところ。この先改善していくと良いけれど、あまり改善しないようなら何かしら方法を考える必要がある。
ふと思い立ってlast (reverse [1..])と叩いてみたけど、結果がかえってくる気配がなかったから^Cで止めた。こういうことは出来ないんだなぁ。

次回

次回はタプルから。

すごいHaskell 読書メモ #1

2015年10月4日日曜日

前置き

3年半ぐらい前にHaskell公式サイトのオンラインドキュメント(どちらかというと仕様書に近い)からHaskellを学ぶという取り組みをしていたのですが見事に頓挫(http://www.muo.jp/2012/04/haskell_03.html)していました。これはその続きの勉強の話です。

すごいH本

(´・_・`)氏訳の「すごいH本」は1年半ほど前に*1物理本を買い、少しずつ読んでいました。
これは評判に違わぬ良い本で、私から(純粋)関数型プログラミングに対する恐怖感というかつらそう苦しそうという先入観を取り払う役目を担ってくれました。型クラスの説明あたりまでしか読み進んでいない状態でずいぶん時間を開けてしまったのですが、その後にScalaの勉強を始めた時に本の説明がすんなり頭に入ってきたのはこの本のおかげだと思っています。
[*1] 確か、tanakhさんにお会いしてその帰りにAmazonで注文したような憶えがあります。

ない…!

1ヶ月少々ぶりに読もうと思ったんですが、どうも見つからない。
自宅の机のうえで手の届きやすい場所へ置いておいたはずだけど全然見つからない。
家の中をひっくり返して探してみても見つからない。
もしや1ヶ月ぐらい前に数日間アメリカへ行ってきた際の移動時間用に持っていったっきりなのでは、と思い始めました。移動中は結局他のことをやっていて読まなかったため、とても記憶が曖昧です。
(中略)
探すのを諦めて達人出版会の電子版を追加購入しました。本をなくしたのは何年ぶりだろう。

そういうわけで

以前取ったメモの再構成を交えつつ勉強していくメモです。

#1 関数の記述と適用の話

1章を読み始める。いきなりイントロへ戻ってこいと言われる。戻ってみる。ちゃんと読んだ記憶ある。手元のbrew環境にあるGHCを最新版へ念のためアップグレードしておく。
→ghci起動に失敗する、と思ったらbrew環境が全体に古かったので丸ごとアップグレードした。このとき、コマンド実行でコケていたけれど単純にgmpのリンクが切れてるっぽかったのでunlinkしてlinkし直したところうまくいった(brewにありがちなこと)。
/=演算子を2値が等しいかの判定に使っててふぁぁぁと思ったけど、これあれか、≠か。5 /= 55 / 5 == 0で0をC的に真偽判定してFalseという例かと読んでから気付いた。
関数が2引数のときは、関数を``で囲うことで中置関数として呼び出せる。例えば92 `div` 10、なるほど。これよく使われるのかな。
関数の適用は優先度的に明確である限りは括弧無しで引数指定するようなので注意していく*2
[*2] ちなみに、試してみると元々中置関数の - や * を``で囲むと単純にparse errorとなる。
文字列リテラル(というのかまだ知らない)をシングルクォートで囲うと、「TemplateHaskellを使おうとしたのん??」と問われるのでそのうち解消しよう(なんかモジュール要るやつをおせっかいしてくれるのかなGHCi)
  • Haskellでのif式にはelse節が必須
  • すべての関数は何らかの値を返す
    • 他の言語で、コードパスによっては値を返すけど他のコードパスでは返さないような、関数シグニチャと挙動の一致しない定義が許されないのと同様、Haskellのifは必ず値を返す式(文ではない)ゆえ、else必須と
ちょい先でシングルクォート(アポストロフィ)あたりの話が出てきた。Haskellで'は特別な意味を持たず、関数名の一部として有効な文字であると。なるほど。
正格版の関数というフレーズが出てきてなんぞとなったけれど、遅延じゃないと書いてあったのでそういうものだろう。'は他に、ちょい変更した関数の意で名前付けに使うこともあると。慣れないので見づらく感じるけれど、たぶん慣れてる人にとっては見れば分かるような使い分けなのだろう。
だいぶ前にやった覚えのある、Haskellの関数名は大文字で始められないよというところまで来た。引数を取らない関数を定義名前と呼ぶと。
関数呼び出し、関数定義と来て次はリストやタプルの話へ進もうというところで今回は時間切れ。続きは次回。