具体的には、既存の無限リストからN番目ごとの要素の新しいリストを生成するにはどうすればよいですか?
例えば。リストが[5, 3, 0, 1, 8, 0, 3, 4, 0, 93, 211, 0 ...]
の場合、3番目の要素ごとに取得すると、このリストは[0,0,0,0,0 ...]
になります。
ドロップを使用した私のバージョン:
every n xs = case drop (n-1) xs of
(y:ys) -> y : every n ys
[] -> []
編集:これは有限リストでも機能します。無限リストの場合のみ、CharlesStewartのソリューションはわずかに短くなります。
Zip
などを使用するすべてのソリューションは、大量の不要な割り当てを行います。関数型プログラマーとして、本当に必要な場合を除いて、割り当てを行わない習慣を身に付けたいと考えています。割り当ては高価であり、割り当てと比較して、他のすべては無料です。また、割り当ては、プロファイラーで見られる「ホットスポット」に表示されるだけではありません。割り当てに注意を払わないと、それはあなたを殺しますどこでも。
今、私はコメンテーターに同意します読みやすさが最も重要なことです。誰も間違った答えを早く得たいとは思わない。しかし、関数型プログラミングでは、複数のソリューションがあり、すべてがほぼ同じように読み取り可能であり、割り当てられるものと割り当てられないものがあります。それらの読み取り可能、非割り当てソリューションを探す習慣を身に付けることが非常に重要です。
オプティマイザーが割り当てを取り除くと思うかもしれませんが、あなたは半分しか正しくありません。 Haskell用の世界最高の最適化コンパイラであるGHCは、要素ごとにペアを割り当てることをなんとか回避しています。 filter
-Zip
コンポジションをfoldr2
に融合します。リスト[1..]
の割り当ては残ります。これはそれほど悪いことではないと思うかもしれませんが、GHCの現在のテクノロジーであるストリームフュージョンはやや脆弱な最適化です。専門家でさえ、コードを見ただけでは、いつ機能するかを正確に予測することは困難です。より一般的なポイントは、割り当てなどの重要なプロパティに関しては、結果を予測できない派手なオプティマイザーに依存したくないということです。同じように読みやすい方法でコードを記述できる限り、これらの割り当てを導入しない方がはるかに良いでしょう。
これらの理由から、drop
を使用したNefrubyrのソリューションがはるかに説得力があると思います。割り当てられる値は、最終的な回答の一部であるmustであるconsセル(:
を含む)のみです。それに加えて、drop
を使用すると、ソリューションが読みやすくなるだけでなく、非常に明確で、明らかに正しいものになります。
職場でこれをテストするものは何もありませんが、次のようなものです。
extractEvery m = map snd . filter (\(x,y) -> (mod x m) == 0) . Zip [1..]
無限のリストでも機能するはずです。
(編集:テストおよび修正されました。)
最初の要素から開始:
everyf n [] = []
everyf n as = head as : everyf n (drop n as)
N番目の要素から開始:
every n = everyf n . drop (n-1)
MHarrisの答えは素晴らしいです。しかし、私は\
の使用を避けたいので、これが私のゴルフです。
extractEvery n
= map snd . filter fst
. Zip (cycle (replicate (n-1) False ++ [True]))
コンパイラーまたはインタープリターはステップサイズを計算します(ゼロベースなので1を引きます)。
f l n = [l !! i | i <- [n-1,n-1+n..]]
mod
を取り除くための代替ソリューション:
extractEvery n = map snd . filter ((== n) . fst) . Zip (cycle [1..n])
私は先日Haskellのバージョンを削除したので、テストされていませんが、以下は他のものよりも少し単純に見えます(Zipとmodを避けるためにパターンマッチングとドロップを活用します):
everynth :: Int -> [a] -> [a]
everynth n xs = y : everynth n ys
where y : ys = drop (n-1) xs
それを行う別の方法:
takeEveryM m lst = [n | (i,n) <- Zip [1..] lst, i `mod` m == 0]
さらに別の:
import Data.List
takeEveryMth m =
(unfoldr g) . dr
where
dr = drop (m-1)
g (x:xs) = Just (x, dr xs)
g [] = Nothing
明示的な再帰は悪です!代わりに、マップ、フィルター、フォールド、スキャン、複製、反復などのライブラリ構造を使用してください。ライブラリを使用している人でもコードが大幅に読みやすくならない限り、コードのモジュール性が低下し、冗長性が増し、推論が難しくなります。
真剣に、これほど単純なタスクでは、明示的に再帰的なバージョンを否定的に投票する必要があります。今、私は私のカーピングのバランスをとるために建設的な何かを言うべきだと思います。
私は地図を使うことを好みます:
すべてのnxs =マップ(xs !!)[n-1、n-1 + n ..]
jaのリスト内包表記ではなく、読者が私が何であるかを心配する必要がないようにします。しかし、いずれにしても、O(n)である可能性がある場合はO(n ^ 2)であるため、おそらくこれが優れています。
すべてのn =マップ(!!(n-1))。反復(ドロップn)
ビューパターンを使用してください!
{-# LANGUAGE ViewPatterns #-}
everynth n (drop (n-1) -> l)
| null l = []
| otherwise = head l : everynth n (tail l)
Nefrubyrの答えの醜いバージョンが保存されているので、コメントは理にかなっています。
everynth :: Int -> [a] -> [a]
everynth n l = case splitAt (n-1) l of
(_, (x:xs)) -> x : everynth n xs
_ -> []
(これは コメント ドロップなしの解決策を求めたことに対する応答でした)
私はこの解決策を見ることができなかったので、:
every _ [] = []
every n (x:xs) = every' n (n-1) (x:xs)
where every' n c [] = []
every' n 0 (x:xs) = x : every' n (n-1) xs
every' n c (x:xs) = every' n (c-1) xs
有限および無限のリストで機能します。
take 15 (every 3 [1..])
-- [3,6,9,12,15,18,21,24,27,30,33,36,39,42,45]
extractEvery n l = map head (iterate (drop n) (drop (n-1) l))
グレッグがほぼ同じ答えを得るのを見るまで、そして私の前に、私は自分自身を誇りに思うつもりでした
foldr
の少しばかげたバージョン:
everyNth n l = foldr cons nil l (n-1) where
nil _ = []
cons x rest 0 = x : rest n
cons x rest n = rest (n-1)
Data.List.HT
from utility-ht
はsieve :: Int -> [a] -> [a]
。
{-| keep every k-th value from the list -}
sieve, sieve', sieve'', sieve''' :: Int -> [a] -> [a]
sieve k =
unfoldr (\xs -> toMaybe (not (null xs)) (head xs, drop k xs))
sieve' k = map head . sliceVertical k
sieve'' k x = map (x!!) [0,k..(length x-1)]
sieve''' k = map head . takeWhile (not . null) . iterate (drop k)
propSieve :: Eq a => Int -> [a] -> Bool
propSieve n x =
sieve n x == sieve' n x &&
sieve n x == sieve'' n x
これは、展開の少し良い使用法のようです:
everyNth :: Int -> [a] -> [a]
everyNth n = unfoldr g
where
g [] = Nothing
g xs = let (ys, zs) = splitAt n xs in Just (head ys, zs)
またはさらに良いことに、Data.List.Spitを使用します。
everyNth n = (map head) . chunksOf n
古い質問ですが、私が思いついたものを投稿します:
everyNth :: [a] -> Int -> [a]
everyNth xs n = [snd x | x <- (Zip [1..] xs), fst x `mod` n == 0]
List引数をInt
の前に置いて、関数とリストをカレーし、必要に応じてInt
sのリストにマップできるようにします。
関連する問題を最初に解決する方がよりエレガントです。インデックスがnで割り切れるすべての要素を保持します。
everyNth n [] = []
everyNth n (x:xs) = x : (everyNth n . drop (n-1)) xs
そして、例を解決するには、
everyNthFirst n = everyNth n . drop (n-1)
everyNthFirst 3 [5, 3, 0, 1, 8, 0, 3, 4, 0, 93, 211, 0 ...]
は[0, 0, 0, ...]
を与えます
受け入れられた答えの醜い、そしてより限定されたバージョン
every :: Eq a => Int -> [a] -> [a]
every n xs = if rest == []
then []
else head rest : every n (tail rest)
where rest = drop (n-1) xs
「ラインゴルフ」の場合、次のように書くことができます。
every n xs = if rest == [] then [] else head rest : every n (tail rest)
where rest = drop (n-1) xs
(不要なEq a
制約があるため、さらに制限されます。)
そして、明らかな条件のない非常に機能的なもの:
everyNth :: Int -> [a] -> [a]
everyNth 0 = take 1
everyNth n = foldr ($) [] . zipWith ($) operations where
operations = cycle $ (:) : replicate (n-1) (const id)
(これは、インデックスがnの倍数であるすべての要素を取得することに注意してください。これは元の質問とは少し異なりますが、変換は簡単です。)
ここでの回答の多くはすでにData.List.unfoldrを使用していますが、他のコンテキストで役立つ可能性のある、やや厄介な展開を記述する興味深い方法を紹介したいと思います。
{-# LANGUAGE TupleSections #-}
import Data.List (unfoldr)
import Data.Maybe (listToMaybe)
every n = unfoldr f . drop (n - 1)
where f xs = (,drop n xs) <$> listToMaybe xs
リストがnullの場合、listToMaybe
はNothing
を返し、fmap
も同様にNothing
になります。ただし、Just
が生成された場合、リストの先頭を先頭のタプルに変換し、残りの値を次の反復で使用することにより、適切なタプルが作成されます。