Haskellを学ぼうとしています。組み込み関数を使用せずにリストから数値を削除する簡単な関数を記述しようとしています(削除...と思います)。簡単にするために、入力パラメーターが整数であり、リストが整数リストであると仮定しましょう。ここに私が持っているコードがあります、次のコードの何が問題になっているのか教えてください
areTheySame :: Int -> Int-> [Int]
areTheySame x y | x == y = []
| otherwise = [y]
removeItem :: Int -> [Int] -> [Int]
removeItem x (y:ys) = areTheySame x y : removeItem x ys
他の人は問題が:
演算子であることは正しいです。とにかく、リストを返すareTheySame
関数は間違ったアプローチだと思います。 ++
演算子に切り替えるのではなく、その関数のより適切な実装は次のようになります。
removeItem _ [] = []
removeItem x (y:ys) | x == y = removeItem x ys
| otherwise = y : removeItem x ys
ご覧のとおり、これはかなり単純な実装です。また、このように考えることは、たくさんのリストを一緒に追加するよりも、プログラムにかかる負担がはるかに少なくなります。怠惰に働くなど、他の利点もあります。
:
演算子は、あなたが思っていることをしません:
(:) :: a -> [a] -> [a]
タイプa
のアイテムを受け取り、タイプa
のリストの先頭に追加します。タイプa
の2つのリストを結合するために使用しています。そのためには、++
:
(++) :: [a] -> [a] -> [a]
また、再帰関数を作成する場合は、終了条件が必要です。だからこれを試してください:
removeItem _ [] = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys
このようにして、リストの最後に到達すると、関数は再帰を停止します。
リスト内包表記としてこれを行うこともできます
delete :: Eq a => a -> [a] -> [a]
delete deleted xs = [ x | x <- xs, x /= deleted ]
関数を1行のコードで記述しました。
remove element list = filter (\e -> e/=element) list
例えば:
remove 5 [1..10]
[1、2、3、4、6、7、8、9、10]
remove 'b' ['a'..'f']
「acdef」
これは、サンプルを機能させるための最小限の修正です。
removeItem :: Int -> [Int] -> [Int]
removeItem _ [] = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys
まず、リストを連結するために++
を使用する必要があります。これは、使用する:
演算子がリストの先頭に要素を1つだけ追加するためです(1つの要素を持つリストの追加や空のリストの追加には使用できません)。最初に、リストの先頭(y
)を削除するアイテムと比較し、areTheySame
を使用してアイテムまたは空のリストを正しく返します。次に、残りのリスト(removeItem
)でys
を再帰的に使用し続けます。結果のリストは、++
を使用して連結する必要があります。
次に、Chris Lutzが述べたように、リストの最後に到達したときの終了条件が必要です。この行を追加することにより、Haskellは空のリストの処理方法を認識します(つまり、何もせず、空のリストを返すだけです)。
Chuckが言ったように、removeItemに比較のタスクを委任せずにこのタスクのコードを簡略化できますが、それ自体を比較して、削除する必要がある場合は要素を破棄し、そうでない場合はリストの先頭に保持します(:
を使用)。いずれの場合も、リストの残りを再帰的に続行します。
-- nothing can be removed from an empty list
-- ==> return empty list and stop recursion
removeItem _ [] = []
-- if the list is not empty, cut off the head in y and keep the rest in ys
-- if x==y, remove y and continue
removeItem x (y:ys) | x == y = removeItem x ys
-- otherwise, add y back and continue
| otherwise = y : removeItem x ys
参考までに、 Data.Listのdelete
でどのように行われるか を参照してください。
areTheySame
をそのままにすることもできますが、空のリストを折りたたむにはconcatMap
で removeItem
を使用する必要があります。
removeItem :: Int -> [Int] -> [Int]
removeItem x xs = concatMap (areTheySame x) xs
または同等に
removeItem :: Int -> [Int] -> [Int]
removeItem x = concatMap (areTheySame x)
関数のタイプはより一般的であることに注意してください:
areTheySame :: (Eq a) => a -> a -> [a]
removeItem :: (Eq a) => a -> [a] -> [a]
これにより、Int
だけでなく、==
が定義されているすべてのタイプのリストからアイテムを削除できます。
これまでに提供されたすべてのソリューションは、最初のメンバーのみを削除するData.List.deleteとは動作が異なると思います。
deleteFromList x xs =
case break (==x) xs of
(_,[]) -> xs
(notsat,sat) -> notsat ++ tail sat
最初のメンバーのみを削除する私の試みでした(まだD.Lのピークに達していません)。
上位の投稿者がどのような行動を望んでいるかは不明です。