Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List Haskell:在索引k处将列表拆分为2_List_Haskell_Split - Fatal编程技术网

List Haskell:在索引k处将列表拆分为2

List Haskell:在索引k处将列表拆分为2,list,haskell,split,List,Haskell,Split,我是哈斯克尔的新手,现在有点麻烦。我正在尝试实现一个函数,它接受一个列表和一个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

我是哈斯克尔的新手,现在有点麻烦。我正在尝试实现一个函数,它接受一个列表和一个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!"
split::[a]->Int->([a],[a])
拆分[]k=错误“空列表!”
拆分(x:[])k=([x],]))
拆分xs k | k>=(长度xs)=错误“数字超出范围!”
|k<0=错误“数字超出范围!”

我真的不知道该怎么分割。任何帮助都将不胜感激。

基本上,当您在列表中循环时,您需要某种方式传递部分进度。我使用了第二个函数,它接受一个累加器参数;它从split调用,然后递归地调用自己。几乎可以肯定有更好的方法

EDIT:删除了所有长度检查,但我相信使用
++
意味着它仍然是O(n^2)


要获得标准的
splitAt
函数的更宽容的行为。

首先,请注意,您试图构造的函数已经在标准库中,在
前奏中-它被称为
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 kxs
。要构造前缀,同样要删除第一个元素,取一个较小的前缀,然后将元素粘回-
take(k+1)(x:xs)==x:take kxs
请参见前奏曲中的
splitAt

ghci> :t flip splitAt
flip splitAt :: [a] -> Int -> ([a], [a])
ghci> flip splitAt  ['a'..'j'] 5
("abcde","fghij")

在构建列表时,消除二次行为的一个常见技巧是向后构建,然后反向构建,修改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!"
split xs k | k<0=错误“数字超出范围!”
拆分xs k=(反向a、b)
哪里
(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=错误“编号超出范围!”
ssplit中的错误检查没有问题,因为除非存在实际错误,否则不会进行检查(先前的模式之一将匹配)

实际上,您可能希望向ssplit添加一些严格性注释来管理堆栈增长,但这是一种进一步的改进。

这是怎么回事:

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")

也许这会有帮助不,不要使用数组进行列表处理!我认为检查每个递归调用的长度是不好的。您正在有效地使算法在时间上成为O(n^2)。只需使用相同的洞察力来考虑它。我更喜欢这种方法。或者仅使用内部函数splitAt:
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")