我对Haskell很新,我遇到了一些麻烦。我正在尝试实现一个带有列表和int的函数。 int应该是索引k,列表被分成一对列表。第一个包含列表的前k个元素,第二个包含k + 1到最后一个元素。这是我到目前为止所做的:
split :: [a] -> Int -> ([a], [a])
split [] k = error "Empty list!"
split (x:[]) k = ([x],[])
split xs k | k >= (length xs) = error "Number out of range!"
| k < 0 = error "Number out of range!"
我实际上无法弄清楚如何进行拆分。任何帮助将不胜感激。
答案 0 :(得分:18)
首先,请注意您尝试构建的函数已经在Prelude
中的标准库中 - 它被称为splitAt
。现在,直接查看它的定义是令人困惑的,因为有两种算法,一种根本不使用标准的递归结构 - splitAt n xs = (take n xs, drop n xs)
- 而且是一种手工优化的算法,使其变得丑陋。前者更直观,因为您只需要使用前缀和后缀并将它们放在一对中。然而,后者教更多,并具有这种整体结构:
splitAt :: Int -> [a] -> ([a], [a])
splitAt 0 xs = ([], xs)
splitAt _ [] = ([], [])
splitAt n (x:xs) = (x:xs', xs'')
where
(xs', xs'') = splitAt (n - 1) xs
基本思想是,如果列表由头部和尾部组成(形式为x:xs
),那么从索引k + 1开始的列表将与列表相同删除第一个元素后,从k开始 - drop (k + 1) (x : xs) == drop k xs
。要构造前缀,您同样删除第一个元素,使用较小的前缀,并将元素重新粘贴在 - take (k + 1) (x : xs) == x : take k xs
上。
答案 1 :(得分:3)
这个怎么样:
splitAt' = \n -> \xs -> (take n xs, drop n xs)
一些测试:
> splitAt' 3 [1..10]
> ([1,2,3],[4,5,6,7,8,9,10])
> splitAt' 0 [1..10]
> ([],[1,2,3,4,5,6,7,8,9,10])
> splitAt' 3 []
> ([],[])
> splitAt' 11 [1..10]
> ([1,2,3,4,5,6,7,8,9,10],[])
> splitAt' 2 "haskell"
> ("ha","skell")
答案 2 :(得分:1)
基本上,当您通过列表递归时,您需要一些传递部分进度的方法。我使用了第二个带累加器参数的函数;它是从split调用的,然后递归调用自身。几乎肯定有更好的方法..
编辑:删除了所有长度检查。但我相信使用++
意味着它仍然是O(n ^ 2)。
split xs k | k < 0 = error "Number out of range!"
split xs k = ssplit [] xs k
ssplit p xs 0 = (p, xs)
ssplit p (x:xs) k = ssplit (p++[x]) xs (k-1)
ssplit p [] k = error "Number out of range!"
获取原始帖子中的行为或
ssplit p [] k = (p,[])
获得标准splitAt
函数的更宽容行为。
答案 3 :(得分:1)
在构建列表时摆脱二次行为的一个常见技巧是向后构建它,然后反转它,修改Mark Reed的解决方案:
split xs k | k < 0 = error "Number out of range!"
split xs k = (reverse a, b)
where
(a,b) = ssplit [] xs k
ssplit p xs 0 = (p, xs)
ssplit p (x:xs) k = ssplit (x:p) xs (k-1)
ssplit p [] k = error "Number out of range!"
ssplit中的错误检查很好,因为不会检查(之前的一个模式会匹配),除非出现实际错误。
在实践中,您可能希望为ssplit添加一些严格注释来管理堆栈增长,但这是一个进一步的改进。
答案 4 :(得分:0)
请参阅前奏中的splitAt
:
ghci> :t flip splitAt
flip splitAt :: [a] -> Int -> ([a], [a])
ghci> flip splitAt ['a'..'j'] 5
("abcde","fghij")