すごい配列楽しく学ぼう     ~ Haskell の配列事情 ~           @xenophobia__        
テーマについて勉強会をやるらしい→ 各 λ 人の好きな題材でいいらしい。→ じゃあ Haskell で何か話そう。→ でもすごい H 本出たし基本的なことはもう話題と して出尽してるよな……→ あれ?この本配列について書いてなくね?→ λ < ...
Haskell とは!   なんてことを話している時間はない。   とりあえず今後の話に関わることとしてこれぐらい。            型に厳しい(強い静的型付けである)                  キャストとかゆとりな事はし...
Haskell の配列について   Haskell では、コレクションは普通リスト型で扱う。   配列はあんまり話題にならない気がする(すごい H     本にも記述は多分ない? RHW にはあった)   調べるといろいろ (Array,...
Agenda   とりあえず普通の配列( Array, UArray )   O(1) で変更できなきゃ配列じゃない! (IOArray,      IOUArray)   型クラスの話をしよう (IArray, MArray)     ...
Agenda   とりあえず普通の配列( Array, UArray )   O(1) で変更できなきゃ配列じゃない! (IOArray,      IOUArray)   型クラスの話をしよう (IArray, MArray)     ...
Haskell 標準の配列を使う   Array, UArray (違いはあとで。)   Array は Data.Array( または Data.Array.IArray) に     ある。   UArray は Data.Array...
Haskell 標準の配列を使う   配列を作るには?    → array 関数を使う(或いは listArray )             array (0, 3) [(0, 1), (1, 2), (2, 4), (3, 9)]  ...
Haskell 標準の配列を使う   型は?   C などと違い、インデックスもいろいろ使える。      → インデックスも型パラメタとして与える。   インデックスが Int で要素が String の Array なら、     A...
Haskell 標準の配列を使う   配列 ary の要素にアクセスしたい → (!) 演算子             例) let x = ary!2 in …                     let x = ary!(0, 3)...
Array/UArray の違いって?   Array 型と UArray 型は、使い方はほぼ同じ。   じゃあ違いは?             Array は Box 化されている             Uarray は Box 化...
Array/UArray の違いって?   つまり            Array は、オブジェクトへのポインタを持った配列            UArray は、値そのものを入れる配列   もっと言うと            A...
Array/UArray の違いって?   Array の要素を参照するときの動作   ポインタが示す先に行き、オブジェクトを返す               そのオブジェクトの値が必要になったら評価する。               ...
なんでそんなことになってんの?   知らん。            
なんでそんなことになってんの?   …… ではさすがにあんまりなので。   Array が正格評価する仕様だと、例えば変数 x, y,      z を用いて          ary = array (1, 3) [(1, x), (2,...
なんでそんなことになってんの?   「 x を配列に入れる」というロジックに「 x を評価す      る」ことは必要か?   たとえば ary のうち一つの要素にしかアクセスしな     いかもしれない。    → 「アクセスしない=使わ...
なんでそんなことになってんの?   λ <使わないのに評価されるのは遅延評価とし     ておかしい!( Haskell は call by need の遅延評     価!)   要素が何であるかの評価は、実際にその要素の値     が必...
UArray(Unboxed Array)   Q. じゃあ UArray って何?   A. Array 遅い!って人のために。   UArray はポインタを介さない分 Array よりアクセ     スが速い&場所とらない。   ...
Agenda   とりあえず普通の配列( Array, UArray )   O(1) で変更できなきゃ配列じゃない! (IOArray,      IOUArray)   型クラスの話をしよう (IArray, MArray)     ...
「 Array と参照透明性」             に関する孔明の罠   さっき (//) という「更新」演算子がでてきた。   でも、 Haskell の関数は(基本的には)参照透明性     が成り立ってなければならない。   参...
「 Array と参照透明性」                     に関する孔明の罠   つまり?   ary = array (1, 2) [(1, 1), (2, 2)]    として、               ary!1  ...
「 Array と参照透明性」              に関する孔明の罠   しかし、 ary を破壊的に更新してしまった場合、     ary!1 が元の値を返すかどうかはわからない。     ary!1 が更新前に評価されれば 1 が返...
「 Array と参照透明性」             に関する孔明の罠   じゃあどういう実装になってんの?     → 単純に、 ary には変更を加えず     ary 用の配列を新しく作る。     → これでどこをいつ参照しても矛盾...
「 Array と参照透明性」                 に関する孔明の罠   …… なにもうまくいってません。            問題 1 :そもそも「更新」じゃなくて「新規作成」演算              子になってる  ...
補足   注 1 )ドキュメントにも Incremental array updates     と書いてあってまぎらわしい。   注 2 ) Data.Array.IArray には、「殆どの IArray の     インスタンスにおい...
うまい解決法はないか   破壊的更新ができれば文句ない。   Haskell で破壊的操作をうまく扱う方法として IO     モナドがある。   IO モナドの文脈中ならうまいこと破壊的操作を扱      える。    → IO モナド...
IOArray/IOUArray   Data.Array.IO にどっちも定義されている。   作成・参照・更新は全て IO アクション。     → アクションの実行順序は保証されるので、参     照と更新の順序が入れかわったりしない...
IOArray/IOUArray   作成: newArray(newArray_, newListArray)              newArray (lb, ub) x  で、インデックスが lb 〜 ub の          ...
IOArray/IOUArray   使い方   IO モナドの中で使う。             do ary ← newArray (1, 10) 0                  x ← readArray ary 1     ...
参照透明性は?   さっきのプログラムの 2 つの readArray ary 1 って     別々の値を返してね?     → readArray ary 1 はあくまで両方「 ary からイン     デックス i の値を取り出す操作」...
Pros & Cons   正真正銘の O(1) 更新が可能!   ただ、 IO に汚染される上、インタフェースが多少め     んどい。              
備考   IOArray 以外にも、 STArray など破壊的代入がで      きる配列はある。   IO モナドと違い ST モナドは外に脱出できるの      で、 IO 操作を他に使わないならこっちのほうが      いいかも。 ...
補足( 1 )   thaw/freeze 関数を用いて普通の配列と可変配列      との相互変換もできる。   thaw (解凍)が Immutable → Mutable の変換   freeze (凍結)が Mutable → I...
補足( 2 )   もちろん thaw も freeze も O(N) 。              ドキュメントにもちゃんと” by taking a complete copy                 of it.” とある。...
Agenda   とりあえず普通の配列( Array, UArray )   O(1) で変更できなきゃ配列じゃない! (IOArray,      IOUArray)   型クラスの話をしよう (IArray, MArray)     ...
抽象化したい!   Uarray と Array 、 IOUArray と IOArray は Box 化 /     非 Box 化の違いでしかない。     → これらの違いを無視した一般的なインタ     フェースの関数を書きたい。  ...
例えば……   sumA : Int 要素を持つ Array の全要素の和                
例えば……   sumA をそのまま UArray に使おうとすると、当然の      ように型エラーで怒られる。               
例えば……   型ごとに関数を用意すればお k !              
いやいや      <「お k !」じゃねえ!       関数本体全く同じじゃねえか!   こういう、形がほとんど同じ関数を連ねるのを     「ボイラープレート( boilerplate )」という     → Haskeller が最も...
IArray/MArray   変更不可配列 (Immutable Array) を抽象する型ク     ラスが IArray   可変配列 (Mutable Array) を抽象する型クラスが     MArray   型クラス: Ia...
IArray/MArray   IArray a e : e 型の要素を持つ変更不可配列 a   Marray a e m : m モナドのコンテクストで変更可能     な、 e 型の要素を持つ配列 a                
例ふたたび   IArray を使って sumA を抽象化   GHC 拡張 FlexibleContexts が必要(理由は後述)                 
例ふたたび   sumA :: IArray a Int => …    → 「 a が Int を要素に持つ変更不可配列」     という型制約をかけている   sumA :: … => a Int Int → Int      → a ...
例ふたたび   Data.Array.IArray にインスタンスとして             IArray Array e             IArray UArray Int    が宣言されている。( e は型変数で、なんで...
補足( 3 )   Q. かける制約は IArray a e でよかったのでは?   A. 実は、 IArray UArray e という要素の型 e につい     て一般化されたインスタンスはない。     (Data.Array.UA...
補足( 4 )   本来、型制約に具体的な型を入れることはできな     い。     → IArray a Int という制約はふつう許されない。   これを許可するのが FlexibleContexts 拡張              ...
総括   Haskell でも普通に配列使える   しかし、速いプログラムにするには工夫が要る     →使えるところでは Unboxed な配列を使う、とか   抽象化もできる     → ただ、なんでもかんでも抽象化すればいいと   ...
より進んだ話題   Haskell には並列処理用行列     repa ( [hackage]http://hackage.haskell.org/packa     ge/repa )という、データ並列プログラミングをサ     ポートす...
Upcoming SlideShare
Loading in...5
×

すごい配列楽しく学ぼう

6,629

Published on

IS2011勉強会/第一回発表スライド

0 Comments
23 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
6,629
On Slideshare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
29
Comments
0
Likes
23
Embeds 0
No embeds

No notes for slide

すごい配列楽しく学ぼう

  1. 1. すごい配列楽しく学ぼう ~ Haskell の配列事情 ~ @xenophobia__   
  2. 2. テーマについて勉強会をやるらしい→ 各 λ 人の好きな題材でいいらしい。→ じゃあ Haskell で何か話そう。→ でもすごい H 本出たし基本的なことはもう話題と して出尽してるよな……→ あれ?この本配列について書いてなくね?→ λ < Haskell の配列も楽しく学ぼう!    
  3. 3. Haskell とは! なんてことを話している時間はない。 とりあえず今後の話に関わることとしてこれぐらい。  型に厳しい(強い静的型付けである)  キャストとかゆとりな事はしない  型が合ってないと絶対許してくれない  参照透明性が(基本的には)成り立つ  IO ・破壊的変更の扱いがめんどい特殊  unsafe ほげほげの話はしない  「型クラス」がある  「型クラス」は共通の性質を持った型の集まり  ある型クラスに関して polymorphic な関数とか書ける    
  4. 4. Haskell の配列について Haskell では、コレクションは普通リスト型で扱う。 配列はあんまり話題にならない気がする(すごい H 本にも記述は多分ない? RHW にはあった) 調べるといろいろ (Array, UArray, IArray, MArray,  IOArray, ...) でてきてよくわからない。 でも……  プロコンとかだと使いたい。  できるだけ速いほうがいい。    
  5. 5. Agenda とりあえず普通の配列( Array, UArray ) O(1) で変更できなきゃ配列じゃない! (IOArray,  IOUArray) 型クラスの話をしよう (IArray, MArray)    
  6. 6. Agenda とりあえず普通の配列( Array, UArray ) O(1) で変更できなきゃ配列じゃない! (IOArray,  IOUArray) 型クラスの話をしよう (IArray, MArray)    
  7. 7. Haskell 標準の配列を使う Array, UArray (違いはあとで。) Array は Data.Array( または Data.Array.IArray) に ある。 UArray は Data.Array.Unboxed にある。    
  8. 8. Haskell 標準の配列を使う 配列を作るには? → array 関数を使う(或いは listArray )  array (0, 3) [(0, 1), (1, 2), (2, 4), (3, 9)]  array ((1, 1), (2, 2)) [((1,1), a), ((1,2), a), ((2,1), z),  ((2,2), d)]  listArray (1, 100) [0, 0 ..] インデックスには、 Int, Char 及びそのタプルなどが 使える。  厳密には Ix 型クラスのインスタンスがすべて使える。 ここでは説明は省略。    
  9. 9. Haskell 標準の配列を使う 型は? C などと違い、インデックスもいろいろ使える。 → インデックスも型パラメタとして与える。 インデックスが Int で要素が String の Array なら、 Array Int String  となる。 インデックスが (Char, Char) で要素が Int の UArray なら、 UArray (Char, Char) Int となる。    
  10. 10. Haskell 標準の配列を使う 配列 ary の要素にアクセスしたい → (!) 演算子  例) let x = ary!2 in …        let x = ary!(0, 3) in … 配列 ary の要素を更新したい → (//) 演算子  例) let ary = ary//[(1, 2), (3, 0)] ­­ ary[1] を 2 に、 ary[3] を 0 に更新  ただし、これには後に述べるように罠が隠れている。    
  11. 11. Array/UArray の違いって? Array 型と UArray 型は、使い方はほぼ同じ。 じゃあ違いは?  Array は Box 化されている  Uarray は Box 化されていない (´ ・ _ ・ `) ? < おまえは なにを いってるんだ    
  12. 12. Array/UArray の違いって? つまり  Array は、オブジェクトへのポインタを持った配列  UArray は、値そのものを入れる配列 もっと言うと  Array は Lazy な配列  UArray は Strict な配列    
  13. 13. Array/UArray の違いって? Array の要素を参照するときの動作 ポインタが示す先に行き、オブジェクトを返す  そのオブジェクトの値が必要になったら評価する。  もちろん、オブジェクトが評価済みならその値自体を 返す。 0 1 未評価 評価済み Eval 2 10 * 10 100 3    
  14. 14. なんでそんなことになってんの? 知らん。    
  15. 15. なんでそんなことになってんの? …… ではさすがにあんまりなので。 Array が正格評価する仕様だと、例えば変数 x, y,  z を用いて ary = array (1, 3) [(1, x), (2, y), (3, z)] のようにに配列を作ると、 x, y, z は配列作成時に 全て評価されなければならない。(評価しないと 格納する値がわからない)    
  16. 16. なんでそんなことになってんの? 「 x を配列に入れる」というロジックに「 x を評価す る」ことは必要か? たとえば ary のうち一つの要素にしかアクセスしな いかもしれない。 → 「アクセスしない=使わない」ってことじゃない の?    
  17. 17. なんでそんなことになってんの? λ <使わないのに評価されるのは遅延評価とし ておかしい!( Haskell は call by need の遅延評 価!) 要素が何であるかの評価は、実際にその要素の値 が必要になるまで遅延するのがよい! てな感じなんじゃないですかね。知らんけど……    
  18. 18. UArray(Unboxed Array) Q. じゃあ UArray って何? A. Array 遅い!って人のために。 UArray はポインタを介さない分 Array よりアクセ スが速い&場所とらない。 速さとヒープ領域を気にするならこっち使いましょ う。    
  19. 19. Agenda とりあえず普通の配列( Array, UArray ) O(1) で変更できなきゃ配列じゃない! (IOArray,  IOUArray) 型クラスの話をしよう (IArray, MArray)    
  20. 20. 「 Array と参照透明性」 に関する孔明の罠 さっき (//) という「更新」演算子がでてきた。 でも、 Haskell の関数は(基本的には)参照透明性 が成り立ってなければならない。 参照透明性 : 関数と引数の組によって、返り値が 一意に決まる。    
  21. 21. 「 Array と参照透明性」 に関する孔明の罠 つまり? ary = array (1, 2) [(1, 1), (2, 2)] として、  ary!1  let ary = ary//[(1, 2)] in ary!1 は同じ値であってほしいなければならない。    
  22. 22. 「 Array と参照透明性」 に関する孔明の罠 しかし、 ary を破壊的に更新してしまった場合、 ary!1 が元の値を返すかどうかはわからない。 ary!1 が更新前に評価されれば 1 が返り、 ary!1 が更新後に評価されれば 2 が返る。 → 参照透明性に反する! ary!1 はいつ評価しても同じ値を返さなければなら ない。    
  23. 23. 「 Array と参照透明性」 に関する孔明の罠 じゃあどういう実装になってんの? → 単純に、 ary には変更を加えず ary 用の配列を新しく作る。 → これでどこをいつ参照しても矛盾は起きない。 やったね(?)    
  24. 24. 「 Array と参照透明性」 に関する孔明の罠 …… なにもうまくいってません。  問題 1 :そもそも「更新」じゃなくて「新規作成」演算 子になってる  問題 2 :計算量 O(sizeof(ary)) →  プロコンでうっかり使ったりすると死ぬ 結論: (//) は更新する演算子ではなく、更新され た配列を新規作成する演算子    
  25. 25. 補足 注 1 )ドキュメントにも Incremental array updates と書いてあってまぎらわしい。 注 2 ) Data.Array.IArray には、「殆どの IArray の インスタンスにおいて更新には O(N) かかる」と 書いてある。 注 3 ) Data.Array.IArray の (//) 関数の仕様自体が O(N) の計算量に本質的に関わっているわけで はない。(実際、もっと速い更新を実現する DiffArray などの IArray インスタンスもある)    
  26. 26. うまい解決法はないか 破壊的更新ができれば文句ない。 Haskell で破壊的操作をうまく扱う方法として IO モナドがある。 IO モナドの文脈中ならうまいこと破壊的操作を扱 える。 → IO モナドの中でしか使えない配列を作れば万 事解決!    
  27. 27. IOArray/IOUArray Data.Array.IO にどっちも定義されている。 作成・参照・更新は全て IO アクション。 → アクションの実行順序は保証されるので、参 照と更新の順序が入れかわったりしない!    
  28. 28. IOArray/IOUArray 作成: newArray(newArray_, newListArray)  newArray (lb, ub) x  で、インデックスが lb 〜 ub の x で初期化された配列を返すアクションを返す。 参照: readArray  readArray ary i で、 ary の i 番地の値を返すアクショ ンを返す。 更新: writeArray  writeArray ary i x で、 ary の i 番地の値を x に変更 するアクションを返す。    
  29. 29. IOArray/IOUArray 使い方 IO モナドの中で使う。  do ary ← newArray (1, 10) 0   x ← readArray ary 1   writeArray ary 1 3   y ← readArray ary 1   ... x, y  には 0, 3 がそれぞれ束縛される。    
  30. 30. 参照透明性は? さっきのプログラムの 2 つの readArray ary 1 って 別々の値を返してね? → readArray ary 1 はあくまで両方「 ary からイン デックス i の値を取り出す操作」を表すアクション なのであって、 x, y を返す関数ではありません。    
  31. 31. Pros & Cons 正真正銘の O(1) 更新が可能! ただ、 IO に汚染される上、インタフェースが多少め んどい。    
  32. 32. 備考 IOArray 以外にも、 STArray など破壊的代入がで きる配列はある。 IO モナドと違い ST モナドは外に脱出できるの で、 IO 操作を他に使わないならこっちのほうが いいかも。  IOArray と使い勝手は一緒    
  33. 33. 補足( 1 ) thaw/freeze 関数を用いて普通の配列と可変配列 との相互変換もできる。 thaw (解凍)が Immutable → Mutable の変換 freeze (凍結)が Mutable → Immutable の変換    
  34. 34. 補足( 2 ) もちろん thaw も freeze も O(N) 。  ドキュメントにもちゃんと” by taking a complete copy  of it.” とある。 こういうときこそ STArray 使うといいかもしれない。  手前味噌ですが STUArray を用いたエラトステネス の篩実装例 http://d.hatena.ne.jp/g940425/20110827/1314442246  後半で unsafe な関数使ってるけど……こういう使い かたなら問題ないはず。    
  35. 35. Agenda とりあえず普通の配列( Array, UArray ) O(1) で変更できなきゃ配列じゃない! (IOArray,  IOUArray) 型クラスの話をしよう (IArray, MArray)    
  36. 36. 抽象化したい! Uarray と Array 、 IOUArray と IOArray は Box 化 / 非 Box 化の違いでしかない。 → これらの違いを無視した一般的なインタ フェースの関数を書きたい。    
  37. 37. 例えば…… sumA : Int 要素を持つ Array の全要素の和    
  38. 38. 例えば…… sumA をそのまま UArray に使おうとすると、当然の ように型エラーで怒られる。    
  39. 39. 例えば…… 型ごとに関数を用意すればお k !    
  40. 40. いやいや <「お k !」じゃねえ! 関数本体全く同じじゃねえか! こういう、形がほとんど同じ関数を連ねるのを 「ボイラープレート( boilerplate )」という → Haskeller が最も嫌うもの 私も大嫌いです 形が同じなら、まとめてかけるはず    
  41. 41. IArray/MArray 変更不可配列 (Immutable Array) を抽象する型ク ラスが IArray 可変配列 (Mutable Array) を抽象する型クラスが MArray 型クラス: Iarray/MArray はそれ自体「型」ではな い( IArray  〜 型の値、は存在しない)    
  42. 42. IArray/MArray IArray a e : e 型の要素を持つ変更不可配列 a Marray a e m : m モナドのコンテクストで変更可能 な、 e 型の要素を持つ配列 a    
  43. 43. 例ふたたび IArray を使って sumA を抽象化 GHC 拡張 FlexibleContexts が必要(理由は後述)    
  44. 44. 例ふたたび sumA :: IArray a Int => … → 「 a が Int を要素に持つ変更不可配列」 という型制約をかけている sumA :: … => a Int Int → Int → a Int Int → Int という型を持つので、 a として当てはまる(= IArray a Int というインス タンスが宣言されている)もの全てに適用可能    
  45. 45. 例ふたたび Data.Array.IArray にインスタンスとして  IArray Array e  IArray UArray Int が宣言されている。( e は型変数で、なんでもよい) → a が Array でも UArray でも型が合う!  IArray Array Int => Array Int Int → Int OK !:インスタンス IArray Array e がある  IArray UArray Int => UArray Int Int → Int OK !:インスタンス IArray UArray Int がある    
  46. 46. 補足( 3 ) Q. かける制約は IArray a e でよかったのでは? A. 実は、 IArray UArray e という要素の型 e につい て一般化されたインスタンスはない。 (Data.Array.UArray のドキュメント参照) → なのでこの例では IArray a Int のインスタンス 宣言を使っており、そのため制約が IArray a Int となっている。  IArray Array e => Array Int Int → Int OK !:インスタンス IArray Array e がある  IArray UArray e => UArray Int Int → Int NG !:インスタンス IArray UArray e はない    
  47. 47. 補足( 4 ) 本来、型制約に具体的な型を入れることはできな い。 → IArray a Int という制約はふつう許されない。 これを許可するのが FlexibleContexts 拡張    
  48. 48. 総括 Haskell でも普通に配列使える しかし、速いプログラムにするには工夫が要る →使えるところでは Unboxed な配列を使う、とか 抽象化もできる → ただ、なんでもかんでも抽象化すればいいと いうわけじゃない。 Lazy/Strict が本質的な場面 もあるだろうし、競技プログラミングでわざわざ 使いもしないのに抽象化した関数を書くのは得 策でない。    
  49. 49. より進んだ話題 Haskell には並列処理用行列 repa ( [hackage]http://hackage.haskell.org/packa ge/repa )という、データ並列プログラミングをサ ポートする特殊なライブラリもある。 → 配列というよりは「行列」 → ついこの間( 2012/9/24 ) GHC­7.6 にも対応 DiffArray なんてのもある( Immutable かつ更新も それなりに速い) [hackage]http://hackage.haskell.org/package/diffa rray    
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×