xs !! nがリストのn番目の要素を与えることは知っていますが、そのリストのn番目の要素を編集する方法がわかりません。リストのn番目の要素を編集する方法、または少なくともヒントを与える方法を教えてください。たとえば、次の2番目の要素 'a' a'e 'を作成するにはどうすればよいですか:[' s '、' t '、' a '、' c '、' k ']?ありがとう。
Haskellは関数型言語であるため、すべてが不変であるため、リスト内の要素を「編集」することはできません。代わりに、次のような新しいリストを作成できます。
take n xs ++ [newElement] ++ drop (n + 1) xs
ただし、Haskellでは推奨されていません。詳細については、この投稿を参照してください: リスト内のHaskell置換要素
多くの言語での一般的な操作は、配列内のインデックス付きの位置に割り当てることです。 pythonでは、次のようになります。
>>> a = [1,2,3,4,5]
>>> a[3] = 9
>>> a
[1, 2, 3, 9, 5]
lens パッケージは、(.~)
演算子を使用してこの機能を提供します。 pythonとは異なり、元のリストは変更されませんが、新しいリストが返されます。
> let a = [1,2,3,4,5]
> a & element 3 .~ 9
[1,2,3,9,5]
> a
[1,2,3,4,5]
element 3 .~ 9
は単なる関数であり、 lens パッケージの一部である(&)
演算子は、単なる逆関数適用です。これは、より一般的な関数適用です。
> (element 3 .~ 9) [1,2,3,4,5]
[1,2,3,9,5]
割り当ては、Traversable
sの任意のネストで完全に正常に機能します。
> [[1,2,3],[4,5,6]] & element 0 . element 1 .~ 9
[[1,9,3],[4,5,6]]
または
> set (element 3) 9 [1,2,3,4,5,6,7]
または、複数の要素に影響を与えたい場合は、次を使用できます。
> over (elements (>3)) (const 99) [1,2,3,4,5,6,7]
> [1,2,3,4,99,99,99]
これはリストだけに限定されるものではありませんが、 Traversable typeclassのインスタンスであるすべてのデータ型で機能します。
たとえば、同じ手法が trees 標準の containers パッケージで機能するとします。
> import Data.Tree
> :{
let
tree = Node 1 [
Node 2 [Node 4[], Node 5 []]
, Node 3 [Node 6 [], Node 7 []]
]
:}
> putStrLn . drawTree . fmap show $ tree
1
|
+- 2
| |
| +- 4
| |
| `- 5
|
`- 3
|
+- 6
|
`- 7
> putStrLn . drawTree . fmap show $ tree & element 1 .~ 99
1
|
+- 99
| |
| +- 4
| |
| `- 5
|
`- 3
|
+- 6
|
`- 7
> putStrLn . drawTree . fmap show $ tree & element 3 .~ 99
1
|
+- 2
| |
| +- 4
| |
| `- 99
|
`- 3
|
+- 6
|
`- 7
> putStrLn . drawTree . fmap show $ over (elements (>3)) (const 99) tree
1
|
+- 2
| |
| +- 4
| |
| `- 5
|
`- 99
|
+- 99
|
`- 99
リストのn番目の要素を編集することはできません。値は不変です。新しいリストを作成する必要があります。ただし、不変性のため、変更された要素の後の部分を元のリストと共有できます。
したがって、リストのn番目の要素に変換を適用する場合(および前後の部分が同一である場合)、3つの部分があります。
front
element
back
。次に、パーツを組み立てます
front ++ transform element : back
ですから、興味深い部分をうまく把握することは残っています。
splitAt :: Int -> [a] -> ([a],[a])
これを行うと、splitAt idx list
はリストの最初の部分を返し、インデックスidx
の前をペアの最初のコンポーネントとして返し、残りを2番目のコンポーネントとして返します。
changeNthElement :: Int -> (a -> a) -> [a] -> [a]
changeNthElement idx transform list
| idx < 0 = list
| otherwise = case spliAt idx list of
(front, element:back) -> front ++ transform element : back
_ -> list -- if the list doesn't have an element at index idx
(注:要素のカウントを0から開始しました。1からカウントを開始する場合は、idx-1
を調整して使用する必要があります。)
次の方法がまだ言及されていないことに驚いたので、さらに参照できるように追加します。
replace index elem = map (\(index', elem') -> if index' == index then elem else elem') . Zip [0..]
> replace 2 'e' "stack"
"steck"
範囲外のインデックスのキャスを処理します。
> replace (-1) 'z' "abc"
"abc"
> replace 0 'z' "abc"
"zbc"
> replace 2 'z' "abc"
"abz"
> replace 3 'z' "abc"
"abc"
SplitAtメソッド(O(2N))より遅くはありません。