データ構造全体をトラバースし、あちこちでいくつかの項目を変更したいとします。これは通常、データ構造をパラメーターとして取り、構造の新しく変更されたバージョンを返す関数によって行われます。
入力のすべてのケースに対して、この関数は、返される新しい値がどのように見えるかを定義します。
Tree
a (単なる値のリスト)を変更する基本的な関数は、変更DataA
された値の新しいリストを返すだけでよいでしょう。これらの値の変更を関数に任せるmodifyA
と、主な変更関数は次のようになります。
-- # function to change a |Tree|
mutate :: Tree -> Tree
mutate as = map mutateA as
-- # (The |map| function applies the |mutateA| function to every
-- # element of |as|, creating a list of all the return values)
次に、mutateA
可能なすべての値を変更するように関数を定義する必要があります。値を処理する関数をDataA
伴うのが最善です。mutateB
DataB
これらの関数は、考えられるさまざまな値のケースを調べて、適切な新しい値を返します。
-- # function to change |DataA| items
mutateA :: DataA -> DataA
-- # A |DataA1| is a |DataA1| with modified values
mutateA (DataA1 bs) = DataA1 (map mutateB bs)
-- # A |DataA3| is a |DataA3| with modified values
mutateA (DataA3 s as) = DataA3 s (map mutateA as)
-- # In the remaining case(s) the value stays the same
mutateA d = d
-- # function to change |DataB| items
mutateB :: DataB -> DataB
mutateB (DataB1 as) = DataB1 (map mutateA as)
mutateB (DataB3 s bs) = DataB3 s (map mutateB bs)
-- # Here comes a real change
mutateB (DataB2 _) = DataB2 "foo"
このようにして、ツリー内のすべての要素に対して新しい要素が計算さDataB2
れ、ツリー内のすべての値が「foo」に置き換えられます。
ウォークスルーする必要がある値のリストを含む 5 つの異なるケースがあるため、比較的冗長ですが、これは Haskell に固有のものではありません。命令型言語では、通常、 への 5 つの呼び出しの代わりに 5 つの for ループを使用しますmap
。
この「オーバーヘッド」を減らすために、データ構造を単純化できるかもしれません。もちろん、これは実際のユースケースに依存しますが、たとえば、Data2
ケースは必要ないかもしれません: と の間に違いはDataA2 "abc"
ありDataA3 "abc" []
ますか?