web-dev-qa-db-ja.com

Haskellで無限リストのN番目の要素をすべて取得するにはどうすればよいですか?

具体的には、既存の無限リストからN番目ごとの要素の新しいリストを生成するにはどうすればよいですか?

例えば。リストが[5, 3, 0, 1, 8, 0, 3, 4, 0, 93, 211, 0 ...]の場合、3番目の要素ごとに取得すると、このリストは[0,0,0,0,0 ...]になります。

33
Linus Arver

ドロップを使用した私のバージョン:

every n xs = case drop (n-1) xs of
              (y:ys) -> y : every n ys
              [] -> []

編集:これは有限リストでも機能します。無限リストの場合のみ、CharlesStewartのソリューションはわずかに短くなります。

59
Nefrubyr

Zipなどを使用するすべてのソリューションは、大量の不要な割り当てを行います。関数型プログラマーとして、本当に必要な場合を除いて、割り当てを行わない習慣を身に付けたいと考えています。割り当ては高価であり、割り当てと比較して、他のすべては無料です。また、割り当ては、プロファイラーで見られる「ホットスポット」に表示されるだけではありません。割り当てに注意を払わないと、それはあなたを殺しますどこでも

今、私はコメンテーターに同意します読みやすさが最も重要なことです。誰も間違った答えを早く得たいとは思わない。しかし、関数型プログラミングでは、複数のソリューションがあり、すべてがほぼ同じように読み取り可能であり、割り当てられるものと割り当てられないものがあります。それらの読み取り可能、非割り当てソリューションを探す習慣を身に付けることが非常に重要です。

オプティマイザーが割り当てを取り除くと思うかもしれませんが、あなたは半分しか正しくありません。 Haskell用の世界最高の最適化コンパイラであるGHCは、要素ごとにペアを割り当てることをなんとか回避しています。 filter-Zipコンポジションをfoldr2に融合します。リスト[1..]の割り当ては残ります。これはそれほど悪いことではないと思うかもしれませんが、GHCの現在のテクノロジーであるストリームフュージョンはやや脆弱な最適化です。専門家でさえ、コードを見ただけでは、いつ機能するかを正確に予測することは困難です。より一般的なポイントは、割り当てなどの重要なプロパティに関しては、結果を予測できない派手なオプティマイザーに依存したくないということです。同じように読みやすい方法でコードを記述できる限り、これらの割り当てを導入しない方がはるかに良いでしょう。

これらの理由から、dropを使用したNefrubyrのソリューションがはるかに説得力があると思います。割り当てられる値は、最終的な回答の一部であるmustであるconsセル(:を含む)のみです。それに加えて、dropを使用すると、ソリューションが読みやすくなるだけでなく、非常に明確で、明らかに正しいものになります。

35
Norman Ramsey

職場でこれをテストするものは何もありませんが、次のようなものです。

extractEvery m = map snd . filter (\(x,y) -> (mod x m) == 0) . Zip [1..]

無限のリストでも機能するはずです。

(編集:テストおよび修正されました。)

16
MHarris

最初の要素から開始:

everyf n [] = []
everyf n as  = head as : everyf n (drop n as)

N番目の要素から開始:

every n = everyf n . drop (n-1)
13
sth

MHarrisの答えは素晴らしいです。しかし、私は\の使用を避けたいので、これが私のゴルフです。

extractEvery n
  = map snd . filter fst
  . Zip (cycle (replicate (n-1) False ++ [True]))
11
yairchu

コンパイラーまたはインタープリターはステップサイズを計算します(ゼロベースなので1を引きます)。

f l n = [l !! i | i <- [n-1,n-1+n..]]

Haskell 98レポート:等差数列

11
ja.

modを取り除くための代替ソリューション:

extractEvery n = map snd . filter ((== n) . fst) . Zip (cycle [1..n])
10
Alexey Romanov

私は先日Haskellのバージョンを削除したので、テストされていませんが、以下は他のものよりも少し単純に見えます(Zipとmodを避けるためにパターンマッチングとドロップを活用します):

everynth :: Int -> [a] -> [a]
everynth n xs = y : everynth n ys
         where y : ys = drop (n-1) xs
10
Charles Stewart

それを行う別の方法:

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
4
primodemus

明示的な再帰は悪です!代わりに、マップ、フィルター、フォールド、スキャン、複製、反復などのライブラリ構造を使用してください。ライブラリを使用している人でもコードが大幅に読みやすくならない限り、コードのモジュール性が低下し、冗長性が増し、推論が難しくなります。

真剣に、これほど単純なタスクでは、明示的に再帰的なバージョンを否定的に投票する必要があります。今、私は私のカーピングのバランスをとるために建設的な何かを言うべきだと思います。

私は地図を使うことを好みます:

すべてのnxs =マップ(xs !!)[n-1、n-1 + n ..]

jaのリスト内包表記ではなく、読者が私が何であるかを心配する必要がないようにします。しかし、いずれにしても、O(n)である可能性がある場合はO(n ^ 2)であるため、おそらくこれが優れています。

すべてのn =マップ(!!(n-1))。反復(ドロップn)

2
Greg M

ビューパターンを使用してください!

{-# 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
                 _           -> []
2
Greg Bacon

(これは コメント ドロップなしの解決策を求めたことに対する応答でした)

私はこの解決策を見ることができなかったので、:

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]
1
Matt Ellen

extractEvery n l = map head (iterate (drop n) (drop (n-1) l))

グレッグがほぼ同じ答えを得るのを見るまで、そして私の前に、私は自分自身を誇りに思うつもりでした

1
Ellery Newcomer

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)
1
Matthias

Data.List.HT from utility-htsieve :: 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
0
Matthias

これは、展開の少し良い使用法のようです:

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
0
idontgetoutmuch

古い質問ですが、私が思いついたものを投稿します:

everyNth :: [a] -> Int -> [a]
everyNth xs n = [snd x | x <- (Zip [1..] xs), fst x `mod` n == 0]

List引数をIntの前に置いて、関数とリストをカレーし、必要に応じてIntsのリストにマップできるようにします。

0
cms_mgr

関連する問題を最初に解決する方がよりエレガントです。インデックスが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, ...]を与えます

0
Matthias

受け入れられた答えの醜い、そしてより限定されたバージョン

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制約があるため、さらに制限されます。)

0
Eugen

そして、明らかな条件のない非常に機能的なもの:

everyNth :: Int -> [a] -> [a]
everyNth 0 = take 1
everyNth n = foldr ($) [] . zipWith ($) operations where
  operations = cycle $ (:) : replicate (n-1) (const id)

(これは、インデックスがnの倍数であるすべての要素を取得することに注意してください。これは元の質問とは少し異なりますが、変換は簡単です。)

0
Matthias

ここでの回答の多くはすでに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の場合、listToMaybeNothingを返し、fmapも同様にNothingになります。ただし、Justが生成された場合、リストの先頭を先頭のタプルに変換し、残りの値を次の反復で使用することにより、適切なタプルが作成されます。

0
N. Prindle