Haskell:解析错误可能不正确的缩进或不匹配的括号

Sea*_*ean 3 haskell

当我尝试使用下面的函数来实现像下面这样的函数时,编译器返回

解析错误(可能不正确的缩进或不匹配的括号)

功能:

演示 8 [1,2,3] 应该返回 [1,2,3,1,2,3,1,2]

 demo :: Int -> [a] -> [a] 
    let n = 0 
    demo arg [] = [] 
    demo arg (x:xs) = 
         if arg <= length (x:xs) then
             take arg (x:xs) 
         else
             let (x:xs) = (x:xs) ++ (x:xs)!!n
             arg = arg - 1
             n = n + 1
             demo arg (x:xs)
Run Code Online (Sandbox Code Playgroud)

我该如何纠正?问候!

jfe*_*ard 6

您正在混合命令式和函数式范式:let n = 0, let (x:xs) = (x:xs) ++ (x:xs)!!n, arg = arg - 1,n = n + 1是(在您的代码中)命令式表达式。您希望n,(x:xs)和的值arg被修改,但函数式编程并不是这样工作的。

您声明的函数是函数:这意味着您不能期望值被修改(我们称之为“副作用”)。您唯一可以做的就是使用从原始参数“动态”计算的新参数调用新函数(或相同函数)。

让我们试着具体一点。

你不能这样做:

arg = arg - 1
demo arg (x:xs)
Run Code Online (Sandbox Code Playgroud)

但你可以这样做:

demo (arg - 1) (x:xs)
Run Code Online (Sandbox Code Playgroud)

后者是demowitharg - 1作为参数的调用。的值arg从未被修改。

您代码中的主要问题是n变量。它应该是0第一次调用,并且每次都应该增加arg < length (x:xs)以将下一个元素添加(x:xs)(x:xs)自身的末尾。因此,列表将以循环方式增长,直到我们可以获取所需数量的元素。

为了达到这个目标,你必须创建一个辅助函数并将递归“移动”到该辅助函数中:

demo :: Int -> [a] -> [a]
demo arg [] = [] 
demo arg (x:xs) = demo' arg 0 (x:xs) -- n = 0
    where 
        demo' :: Int -> Int -> [a] -> [a]
        demo' arg n l = if arg <= length l 
                        then take arg l 
                        else 
                            -- call demo' with new parameters:
                            -- (x:xs) = (x:xs) ++ [(x:xs)!!n]
                            -- arg = arg - 1
                            -- n = n + 1 
                            demo' (arg-1) (n+1) ((x:xs) ++ [(x:xs)!!n]) ```
Run Code Online (Sandbox Code Playgroud)

现在,根据您给出的示例,arg您要创建的列表中的元素数量是常数,因此不应减少。而且您不需要解构(x:xs). 最后,稍微清理一下,你有:

demo :: Int -> [a] -> [a]
demo arg [] = [] 
demo arg l = demo' arg 0 l
    where 
        demo' :: Int -> Int -> [a] -> [a]
        demo' arg n l = if arg <= length l 
                        then take arg l 
                        else demo' arg (n+1) (l ++ [l!!n])
Run Code Online (Sandbox Code Playgroud)

有一个更好的方法来实现这一点 ( demo n=take n.cycle),但我试图接近你的原始实现。


eph*_*ent 3

你不能写let n = 0在顶层。一旦删除它,else let代码的该部分也不会正确缩进,因为它的使用(在块之外do)总是let ... in ...,并且该部分的内容let必须同样缩进。即使它被格式化,let也是递归的,所以arg = arg - 1意味着一个值比它本身小一,这不能计算。

现在,这个函数实际上做了两件事:循环遍历列表的所有元素,并将其限制为给定的长度。这两个都已经在标准库中可用。

demo n xs = take n (cycle xs)
Run Code Online (Sandbox Code Playgroud)

如果你想自己写的话,类似的细分也是合理的。

demo :: Int -> [a] -> [a]
-- Handle negative n, so we can assume it's positive below
demo n _ | n < 0 = []
-- Similarly, handle empty list, so we can assume it's non-empty below
demo _ [] = []
-- Given a positive n and non-empty list, start cycling list with limit n.
demo n list = demo' n list
  where
    -- Limit has been reached, stop.
    demo' 0 _ = []
    -- List is exhausted, cycle back to the start.
    demo' m [] = demo' m list
    -- Take the next element of the list and continue.
    demo' m (x:xs) = x : demo' (m-1) xs
Run Code Online (Sandbox Code Playgroud)

请注意,没有必要使用length,这是一件好事!length在无限列表上发散,而这可以优雅地处理它们。