【问题标题】:Haskell: parse error possibly incorrect indentation or mismatched bracketsHaskell:解析错误可能不正确的缩进或不匹配的括号
【发布时间】:2018-01-21 07:24:23
【问题描述】:

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

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

功能:

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

我该如何纠正? 问候!

【问题讨论】:

  • possibly incorrect indentationlet..in 块看起来缩进不正确。尝试用它的缩进来编译代码。
  • let arg = arg-1let n=n+1 是递归定义,将 argn 定义为无限循环计算。你不想要那个。您正在尝试在 Haskell 中改变变量,而 Haskell 的设计目的是使这成为不可能。这种方法对于在 FP 语言中工作来说太重要了。粗略地说,“更改”变量的唯一方法是调用具有新值的函数,例如。 f 0 = 0 ; f n = f (n-1) 定义了一个递归,它“减少”n 直到它达到 0。

标签: haskell


【解决方案1】:

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

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

让我们试着具体一点。

你不能这样做:

arg = arg - 1
demo arg (x:xs)

但你可以这样做:

demo (arg - 1) (x:xs)

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

代码中的主要问题是n 变量。第一次调用时它应该是0,并且每次arg &lt; 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]) ```

现在,从您给出的示例中,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])

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

【讨论】:

  • 感谢杰弗拉德!你的代码正是我的意思。我认为问题是由变量 n 引起的,但我不知道如何解决这个问题,感谢您回答我的问题。
  • @Sean 欢迎您。您应该接受我的回答或 ehemient 的回答。
【解决方案2】:

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

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

demo n xs = take n (cycle xs)

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

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

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

【讨论】:

    猜你喜欢
    • 2014-05-05
    • 2015-01-26
    • 1970-01-01
    • 2021-01-25
    • 2022-09-28
    • 1970-01-01
    • 2017-06-06
    • 2016-09-28
    • 2021-07-02
    相关资源
    最近更新 更多