Open7

Haskell勉強ノート

スズキサトシスズキサトシ

気まぐれにHaskell勉強してる。ダラダラやるといつになっても身につかないから、2時間ぐらいでざっと全体像をつかもう。
添字アクセスはindex too largeでExceptionになるっていうのが、結局そこでバグりそうだなぁという感覚

スズキサトシスズキサトシ
ghci> head [1]

<interactive>:27:1: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘head’
    (imported from Prelude, but defined in GHC.List):
    "This is a partial function, it throws an error on empty lists. Use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."

Data.List.NonEmpty っていうのがあるのか。これはちょっと気になる。

スズキサトシスズキサトシ

出た、リスト内包表記。名前しかしらない。

ghci>
[x*2 | x <- [1..10]]

take 10 [x*x | x <- [1..]]

[x*2 | x <- [1..10], x*2 >= 12]

これってjsやrubyのmap, filterでも実質的には似たような事ができるから、遅延評価の面白さを除けば、現代的にはさほど珍しくもない…か?(本質は違うという声も聞こえてくるが、実質は同じような…)

実際こうかいてるし。

述語を使ってリストを間引くことをフィルタするといいます

スズキサトシスズキサトシ

1.6.3 直角三角形を見つける

ちょうどページの区切りだったこともあり、以下のように書いてみたら停止しなかった。無限リストの理解が甘いみたい

[ (x,y,z) | x <- [1..], y <- [1..], z <- [1..], x+y+z == 24, x <= 10, y <= 10, z <= 10, xx + y+y == zz]

※よくみたら y*y であるべき部分を y+y と書いていた。が、それに限らずこのプログラムは正しく動作しない。

[ (x,y,z) | x <- [1..10], y <- [1..10], z <- [1..10], x+y+z == 24, xx+yy == z*z]

これなら動作する

[ (x,y,z) | x <- [1..], y <- [1..], z <- [1..], x <= 10, y <= 10, z <= 10, x+y+z == 24, xx + yy == z*z]

このように順序を変えても動くわけではない。

模範解答?は以下らしい。

[ (a,b,c) | c <- [1..10], a <- [1..c], b <- [1..a], a^2 + b^2 == c^2, a+b+c == 24]

なんとなく評価の順序がわかってきた。

[ (a,b,c) | c <- [1..], a <- [1..c], b <- [1..a], a^2 + b^2 == c^2, a+b+c == 24, c <= 10]

…としても停止はしてくれないらしい。 cが11を超えたら打ち切っていいというのは代数的な理解でしかなく、ここでの c <= 10 の評価から機械的に自明ではないということなんだろう。

スズキサトシスズキサトシ

自分

This is a partial function, it throws an error on empty lists とはなんですか?

ChatGPT

「This is a partial function, it throws an error on empty lists」というフレーズは、特定の関数が部分関数であることを指し、空のリストに対して適用された場合にエラーを投げることを意味します。ここでの「部分関数」という用語は、関数が定義されている入力の全範囲にわたって有効な値を返さない場合を指します。つまり、関数には適用できない入力値が存在し、そのような入力が与えられた場合にはエラーが発生します。

例えば、Haskellの標準ライブラリにはhead関数があり、これはリストの最初の要素を返します。しかし、head関数は空のリストに対して適用できないため、部分関数とみなされます。空のリストにheadを適用すると、実行時エラーが発生します。これは、head関数が空のリストに対して定義されていないためです。

部分関数は、プログラムの信頼性を低下させる可能性があるため、避けるべきとされることが多いです。これは、実行時に予期せぬエラーが発生するリスクがあるためです。このような問題を避けるためには、完全関数を使用することが推奨されます。完全関数は、すべての有効な入力に対して値を返す関数です。空のリストを安全に扱うためには、例えばMaybe型を返す関数を使用することができます。これにより、関数が値を返せない場合でも、エラーを発生させることなく、適切な処理を行うことができます。

部分関数と完全関数、重要そう

スズキサトシスズキサトシ

OK

read "True" :: Bool
read "12982" :: Int
read "12982" :: Float
read "12982" :: Double
12982.0

NG

read 1
read "True" :: String
read "True" :: Int

型推論でもいいらしい

read "True" || False
read "8.2" + 3.8

NG

read "8.2" + 1

こっちだと1が先にIntになってしまうんだろう。 Int | Floatとして曖昧なままreadの評価に向かうわけではないみたい。数値リテラルの型推論は限定的な模様。