結城浩のHaskell日記 RSSフィード

2006-06-11

let式とwhere節 let式とwhere節 - 結城浩のHaskell日記 を含むブックマーク

ふつけるp.187まで読みました。

  • let式は式なので値を持つ。
  • where節は節なので値は持たない。
  • let式は変数の定義用に使うことが多い。
  • where節は関数の定義用に使うことが多い。

なるほど。where節はよく使うけれど、letってあまり使ったことないなあ。

結合性 結合性 - 結城浩のHaskell日記 を含むブックマーク

ふつけるp.183まで読みました。

  • 優先順位と結合性
  • 左結合・右結合だけではなく、非結合というのもある。
  • あ、p.184に出てきた。ユーザ定義の演算子のデフォルトは優先順位9の左結合。

アズパターン アズパターン - 結城浩のHaskell日記 を含むブックマーク

あずまんがアズパターンの練習です。

tails' :: [a] -> [[a]]
tails' [] = [[]]
tails' as@(x:xs) = as : tails' xs

実行結果です。

*Main> tails' "hello"
["hello","ello","llo","lo","o",""]

アズパターンは「変数1@変数2@パターン」のように複数個おけます。

以下、無意味な練習。

dtails :: [a] -> [[a]]
dtails [] = [[],[]]
dtails bs@as@(x:xs) = as : bs : dtails xs

実行結果です。

*Main> dtails "hello"
["hello","hello","ello","ello","llo","llo","lo","lo","o","o","",""]

データコンストラクタによるパターン データコンストラクタによるパターン - 結城浩のHaskell日記 を含むブックマーク

ふつけるp.173まで読みました。

[]は空リストを作るパターンで、:はリストを作成するパターンです。

head' :: [a] -> a
head' [] = error "empty list"
head' (x:xs) = x

(x:xs)は((:) x xs)のように書いてもよいはずだと思いました。

head'' :: [a] -> a
head'' [] = error "empty list"
head'' ((:) x xs) = x

実行結果です。

*Main> head'' ""
*** Exception: empty list
*Main> head'' "Hello"
'H'

よしよし。

_パターン _パターン - 結城浩のHaskell日記 を含むブックマーク

これはエラー。

foo x x = "Hello"

こんなエラー。

a.hs:1:
    Conflicting definitions for `x'
    In the definition of `foo'

これならエラーにならない。_パターンは変数を束縛しないから。

foo _ _ = "Hello"

GHCの-Wオプション GHCの-Wオプション - 結城浩のHaskell日記 を含むブックマーク

ふつけるp.168まで読みました。

GHCで-Wオプションをつけると、パターンマッチで実行時エラーになるかどうか検出できます。

module Main where

main = do print (foo 0)

foo x | x > 0 = 1
foo x | x < 0 = negate 1
> ghc -W a.hs -o a

a.hs:5:
    Warning: Pattern match(es) are non-exhaustive
             In the definition of `foo': Patterns not matched: _

とやって気がついたのだけれど、ガードがある場合は無条件で警告でるのかもね。

試してみよう。

module Main where

main = do print (foo 0)

foo x | True = x

うーん、↑だと警告は出ない。

module Main where

main = do print (foo 0)

foo x | x > 0 || x <= 0 = x

うーん、↑だと警告が出る。

if式 if式 - 結城浩のHaskell日記 を含むブックマーク

ふつけるp.167まで読みました。

abs' x = if x > 0 then x else - x

オフサイドルール オフサイドルール - 結城浩のHaskell日記 を含むブックマーク

ふつけるp.165まで読みました。

  • オフサイドルール
  • オフサイドライン: let, where, do, ofの次の単語の場所
  • 式の継続
a = x + y + z
  where
    x = 1
    y = 2 + 3 + 4 + 5 + 6
        + 7 + 8 + 9
    z = 10

関数の無限リスト 関数の無限リスト - 結城浩のHaskell日記 を含むブックマーク

nobsunのrmapのエントリを読んでいて、(いまさらながら)はっと気づいた。

map (+) [1..]

これって、部分適用になるから、[(1+),(2+),(3+),(4+),... ]っていう「関数の無限リスト」になるんだ…。

関数を普通のデータのように扱うっていう感覚、まだまだ身についていないなあと実感した次第。

Haskellとメモ化 Haskellとメモ化 - 結城浩のHaskell日記 を含むブックマーク

ひげぽんさんの「Gaucheのforceはメモする」というのを読んで「そういえば、Haskellの場合には明示的なメモ化の必要はないんじゃないか?」と思いました。

なぜそう思ったかというと、そもそもHaskellには代入がないので、xの値はいつも同じだからです(参照透過性)。つまり「いつもメモ化されている」と思っていてよいのでは?

…とここまで考えてからグーグルで「Haskell memoise」を検索。すると(予想通り)sampou.orgが見つかる。ところが「WWW.SAMPOU.ORG server clashed」というメッセージが。大丈夫でしょうか?

グーグルのキャッシュにリンク。

うーむ。メモ化が不要なわけじゃないのか…まだよくわからず。眠いのでまた今度。

一つのデータに関数リストをapply 一つのデータに関数リストをapply - 結城浩のHaskell日記 を含むブックマーク

map f xsによって、xsの各要素xに対してf xをしたリストが得られるなら、それの逆(というか双対?)のようなこともできますよね。

つまり、fsの各要素fに対して決まったxを与えてリストにするようなの。仮にrmapとしてみます。

rmap :: a -> [a -> b] -> [b]
rmap x [] = []
rmap x (f:fs) = f x : rmap x fs

a = (1 +)
b = (2 *)
c = \x -> x * x
d = \x -> x * x + 1

z = rmap 10 [a,b,c,d]
-- [11,20,100,101]になる

高階に考えると、mapを使えばよいはず。

rmap :: a -> [a -> b] -> [b]
rmap x fs = map (\f->f x) fs

eta reductionを行う。

rmap :: a -> [a -> b] -> [b]
rmap x = map (\f->f x)

追記:nobsunからコメントをもらった。

うーん、すごいな。

rmap :: a -> [a -> b] -> [b]
rmap = map . flip ($)

はじめ、さっぱりわかりませんでした。小一時間考えました。得た知識を以下に列挙。

  • そうか、($)も関数なんだ。
  • map . flip ($)は、(map.flip) ($)ではなく、map . (flip ($)) だ。(ここを誤解していた。GHCi の :i でチェックしてわかった)
  • map . flip ($)は flip.mapではない!(ここも誤解していた。mapの入力をひっくりかえすのではなく、mapが行う個々のapplyをひっくり返すのだ)
  • flip ($) は、関数->入力を入力->関数にする。だからmapと合成すれば、「一つのデータと関数リスト」が扱えるのか(直感的理解)
    • ($) :: (a -> b) -> a -> b
    • flip ($) :: a -> (a -> b) -> b

ここでふと考えた。($)の型の、

  • ($) :: (a -> b) -> a -> b

っていうのは、

  • ($) :: (a -> b) -> (a -> b)

って考えても良いよね。ということは、

  • id :: a -> a

と同じではあるまいか。ということは、($)の代わりにidでも動くのではないか。つまり、こういうこと。

rmap :: a -> [a -> b] -> [b]
rmap = map . flip id

試してみよう。

a = (1 +)
b = (2 *)
c = \x -> x * x
d = \x -> x * x + 1

z = rmap 10 [a,b,c,d]

実行結果です。

*Main> z
[11,20,100,101]

やたっ! ……でも、正しいのだろうか。

コメント コメント - 結城浩のHaskell日記 を含むブックマーク

ふつけるp.162まで読んだ。

nobsunnobsun2006/06/11 02:40HaHaH! 6/11 分を修正するつもりで、6/10 分に上書きしてしまい、あわてて書きなおしたけど、下のトラックバックのリンクがおかしくなってしまいました。ゴメンなさい。

hyukihyuki2006/06/11 06:39よくあることです~。わざわざありがとうございます。私も、トラックバック何度も飛ばしてますです~。ごめんなさい。

airoboairobo2006/06/11 16:55not(x>0)とx<=0が同じかどうかは保証されてないのではないでしょうか.A Gentle Introduction to HaskellのMonadsの項に似たような記述があったと思います.

..2006/07/18 03:41習作ですが、rmapの変換工程。
rmap x = map (\f -> f x)
-- 1. fを消す。id と flip を使う
rmap x = map (\f -> id f x)
rmap x = map (\f -> flip id x f)
rmap x = map (flip id x)
-- 2. xを消す。. (関数合成)を使う
rmap x = map ((flip id) x)
rmap x = (map . (flip id)) x
rmap = (map . (flip id))
rmap = map . flip id

kaekikaeki2011/10/10 18:17rmap 10 [a,b,c,d] => map ($ 10) [a, b, c, d]