web-dev-qa-db-ja.com

Haskellにはリストスライス(Pythonなど)がありますか?

Haskellには Python List Slices と同様の構文糖がありますか?

たとえばPythonでは:

x = ['a','b','c','d']
x[1:3] 

インデックス1からインデックス2までの文字を含めます(またはインデックス3を除外します)。

['b','c']

Haskellが(!!)特定のインデックスの関数ですが、同等の「スライス」またはリスト範囲関数はありますか?

39
Jon W

リストをスライスする組み込み関数はありませんが、droptakeを使用して自分で簡単に作成できます。

slice :: Int -> Int -> [a] -> [a]
slice from to xs = take (to - from + 1) (drop from xs)

Haskellのリストは単一リンクリストであるため(pythonリストは配列です)、そのようなサブリストを作成すると、O(to)ではなくO(to - from)になることに注意してください。 pythonのように(もちろん、リスト全体が実際に評価されると仮定すると、そうでなければHaskellの遅延が有効になります)。

56
sepp2k

Python "lists"(他の人が言うように、これはリストではありません)と一致させようとしている場合は、Haskell vector パッケージを使用することをお勧めします slice が組み込まれています。また、Vector並列評価 でもかまいません。

35

構文糖なし。必要な場合は、takedropを使用できます。

take 2 $ drop 1 $ "abcd" -- gives "bc"
16
Chuck

含まれているとは思いませんが、かなり簡単に記述できます。

slice start end = take (end - start + 1) . drop start

もちろん、startendがインバウンドであり、end >= start

9
ScottWest

Pythonスライスもステップをサポートしています:

>>> range(10)[::2]
[0, 2, 4, 6, 8]
>>> range(10)[2:8:2]
[2, 4, 6]

Dan Burtonの N番目の要素ごとにドロップ に触発されたので、ステップ付きのスライスを実装しました。無限リストで動作します!

takeStep :: Int -> [a] -> [a]
takeStep _ [] = []
takeStep n (x:xs) = x : takeStep n (drop (n-1) xs)

slice :: Int -> Int -> Int -> [a] -> [a]
slice start stop step = takeStep step . take (stop - start) . drop start

ただし、Pythonは、負の開始と停止(リストの最後から数えます)と負のステップ(リストを逆にし、停止が開始になり、逆も同様です)をサポートします。

from pprint import pprint # enter all of this into Python interpreter
pprint([range(10)[ 2: 6],     # [2, 3, 4, 5]
        range(10)[ 6: 2:-1],  # [6, 5, 4, 3]
        range(10)[ 6: 2:-2],  # [6, 4]      
        range(10)[-8: 6],     # [2, 3, 4, 5]
        range(10)[ 2:-4],     # [2, 3, 4, 5]
        range(10)[-8:-4],     # [2, 3, 4, 5]
        range(10)[ 6:-8:-1],  # [6, 5, 4, 3]
        range(10)[-4: 2:-1],  # [6, 5, 4, 3]
        range(10)[-4:-8:-1]]) # [6, 5, 4, 3]]

Haskellでそれを実装するにはどうすればよいですか?ステップが負の場合はリストを逆にし、負の場合はリストの最後からカウントを開始および停止する必要があります。結果のリストにはインデックスの要素が含まれている必要があることに注意してくださいstart <= k <stop(正のステップあり)またはstart> = k> stop(負のステップあり) 。

takeStep :: Int -> [a] -> [a]
takeStep _ [] = []
takeStep n (x:xs)
  | n >= 0 = x : takeStep n (drop (n-1) xs)
  | otherwise = takeStep (-n) (reverse xs)

slice :: Int -> Int -> Int -> [a] -> [a]
slice a e d xs = z . y . x $ xs -- a:start, e:stop, d:step
  where a' = if a >= 0 then a else (length xs + a)
        e' = if e >= 0 then e else (length xs + e)
        x = if d >= 0 then drop a' else drop e'
        y = if d >= 0 then take (e'-a') else take (a'-e'+1)
        z = takeStep d

test :: IO () -- slice works exactly in both languages
test = forM_ t (putStrLn . show)
  where xs = [0..9]
        t = [slice   2   6   1  xs, -- [2, 3, 4, 5]
             slice   6   2 (-1) xs, -- [6, 5, 4, 3]
             slice   6   2 (-2) xs, -- [6, 4]
             slice (-8)  6   1  xs, -- [2, 3, 4, 5]
             slice   2 (-4)  1  xs, -- [2, 3, 4, 5]
             slice (-8)(-4)  1  xs, -- [2, 3, 4, 5]
             slice   6 (-8)(-1) xs, -- [6, 5, 4, 3]
             slice (-4)  2 (-1) xs, -- [6, 5, 4, 3]
             slice (-4)(-8)(-1) xs] -- [6, 5, 4, 3]

このアルゴリズムは正の引数が与えられた無限リストでも機能しますが、負のステップでは空のリストを返し(理論的には逆のサブリストを返す可能性があります)、負の開始または停止では無限ループに入ります。したがって、負の引数には注意してください。

4

同様の問題があり、リスト内包表記を使用しました:

-- Where lst is an arbitrary list and indc is a list of indices

[lst!!x|x<-[1..]] -- all of lst
[lst!!x|x<-[1,3..]] -- odd-indexed elements of lst
[lst!!x|x<-indc]

おそらく、Pythonのスライスほど整然とはしていませんが、それでうまくいきます。 indcは任意の順序でかまいませんが、連続している必要はありません。

前述のように、Haskellがリンクリストを使用すると、この関数は次のようになりますO(n)ここで、nは最大インデックスに依存するpythonのスライスではなく、アクセス値の数アクセスされます。

免責事項:私はまだHaskellに不慣れであり、訂正があれば歓迎します。

4
Ezry

HaskellでPython(mからnまで)の範囲)をエミュレートする場合、ドロップとテイクの組み合わせを使用します。

Pythonの場合:

print("Hello, World"[2:9])  # prints:  "llo, Wo"

Haskellの場合:

print (drop 2 $ take 9 "Hello, World!")  -- prints:  "llo, Wo"
-- This is the same:
print (drop 2 (take 9 "Hello, World!"))  -- prints:  "llo, Wo"

もちろん、これを関数にラップして、Pythonのように動作させることもできます。たとえば、!!!演算子を次のように定義したとします。

(!!!) array (m, n) = drop m $ take n array

その後、次のようにスライスすることができます:

"Hello, World!" !!! (2, 9)  -- evaluates to "llo, Wo"

次のような別の関数で使用します。

print $ "Hello, World!" !!! (2, 9)  -- prints:  "llo, Wo"

ジョンW.

3
J-L

これを行う別の方法は、Data.Listの関数splitAtを使用することです-takedropを使用するよりも読みやすく理解しやすいと思います-しかし、それは単なる個人的な好みです:

import Data.List
slice :: Int -> Int -> [a] -> [a]
slice start stop xs = fst $ splitAt (stop - start) (snd $ splitAt start xs)

例えば:

Prelude Data.List> slice 0 2 [1, 2, 3, 4, 5, 6]
[1,2]
Prelude Data.List> slice 0 0 [1, 2, 3, 4, 5, 6]
[]
Prelude Data.List> slice 5 2 [1, 2, 3, 4, 5, 6]
[]
Prelude Data.List> slice 1 4 [1, 2, 3, 4, 5, 6]
[2,3,4]
Prelude Data.List> slice 5 7 [1, 2, 3, 4, 5, 6]
[6]
Prelude Data.List> slice 6 10 [1, 2, 3, 4, 5, 6]
[]

これは

let slice' start stop xs = take (stop - start) $ drop start xs

これは確かにより効率的ですが、リストが前半分と後ろ半分に分割されているインデックスについて考えるよりも少し混乱します。

3
ely

既存のData.Vector.sliceData.Vector.fromListおよびData.Vector.toListhttps://stackoverflow.com/a/8530351/9443841 を参照)

import Data.Vector ( fromList, slice, toList )
import Data.Function ( (&) )

vSlice :: Int -> Int -> [a] -> [a]
vSlice start len xs =
    xs
        & fromList 
        & slice start len
        & toList 
1
Lo HaBuyshan

私は、Pythonのリストスライスのように、負の数でも機能するこのコードを記述しましたが、リストのスライスとは無関係であるリストの逆転を除きます。

slice :: Int -> Int -> [a] -> [a]

slice 0 x arr
  | x < 0 = slice 0 ((length arr)+(x)) arr
  | x == (length arr) = arr
  | otherwise = slice 0 (x) (init arr)

slice x y arr
  | x < 0 = slice ((length arr)+x) y arr 
  | y < 0 = slice x ((length arr)+y) arr
  | otherwise = slice (x-1) (y-1) (tail arr)

main = do
  print(slice (-3) (-1) [3, 4, 29, 4, 6]) -- [29,4]
  print(slice (2) (-1) [35, 345, 23, 24, 69, 2, 34, 523]) -- [23,24,69,32,34]
  print(slice 2 5 [34, 5, 5, 3, 43, 4, 23] ) -- [5,3,43]


1
Sapphire_Brick

明らかに、私のfoldlバージョンはテイクドロップアプローチに負けますが、誰かがそれを改善する方法を考えているのでしょうか?

slice from to = reverse.snd.foldl build ((from, to + 1), []) where
   build res@((_, 0), _) _ = res  
   build ((0, to), xs) x = ((0, to - 1), x:xs)  
   build ((from, to), xs) _ = ((from - 1, to - 1), xs)
0
Landei
sublist start length = take length . snd . splitAt start

slice start end = snd .splitAt start . take end
0
Meow