选择列表的第二个最后一个元素

时间:2015-07-17 20:50:44

标签: haskell

过了一段时间,我决定回去学习一些函数式编程。我这次决定选择Haskell,因为它的功能和语法。

目前我正在做一些练习而且我被卡住了。我想编写一个从列表中选出第二个元素的函数,即给定[1,2,3,4]它将为3。

这是我的功能。

lastButOne xs 
    | null xs = xs 
    | length xs == 1 = xs 
    | length xs == 2 = lastButOne head xs
    | otherwise = lastButOne tail xs

不幸的是,它会产生一些错误。

Couldn't match expected type `[a]' with actual type `[a1] -> [a1]'
Relevant bindings include
  xs :: [a] (bound at lastButOne.hs:1:12)
  lastButOne :: [a] -> [a] (bound at lastButOne.hs:1:1)
Probable cause: `tail' is applied to too few arguments
In the first argument of `lastButOne', namely `tail'
In the expression: lastButOne tail xs

我尝试了一些兼顾性,比如(头部xs)和(尾部xs),但它没有帮助。

   Occurs check: cannot construct the infinite type: a ~ [a]
   Expected type: [[a]]
     Actual type: [a]
   Relevant bindings include
     xs :: [a] (bound at lastButOne.hs:1:12)
     lastButOne :: [a] -> [a] (bound at lastButOne.hs:1:1)
   In the first argument of `head', namely `xs'
   In the first argument of `lastButOne', namely `(head xs)'

后续: 或者我应该写后续行动。好吧,我的原始想法是编写一个函数,如果列表长度为1,则生成head元素。因此,根据李的解释,很容易想出以下内容:

lastPattern :: [a] -> Maybe a
lastPattern [] = Nothing
lastPattern [x] = Just x
lastPattern [x,_] = Just x
lastPattern (_:xs) = lastPattern xs

这是第一个问题。 条件[x]和[x,_] 的模式是什么?

接下来我要做的是用反向编写相同的函数(正如Paul Johnson所指出的那样)。我很快想出了head (tail (reverse [1,2,3,4])),它似乎在REPL中运行良好。但是当我开始编写代码时,我最终得到了

 lastRev :: [a] -> a
lastRev xs 
    | null xs = error "empty list"
    | length xs == 1 = error "too short"
    | otherwise = head (tail (reverse xs))

因为head函数是head :: [a] -> a。上面的功能对我来说有点乱,所以说。 有什么方法可以让它成为:: [a] -> Maybe a吗?这是第二个问题。

最后但并非最不重要 - 第三个问题。 哪个功能在性能方面更好?如何在Haskell中测量它?

3 个答案:

答案 0 :(得分:5)

(head xs)会返回a,您尝试将其传递给需要lastButOne参数的[a]。在这种情况下,您可以直接返回head xs。前两个案例也有问题,因为它们返回一个列表,而元素是必需的。由于在这种情况下没有这样的元素,您可能会返回错误:

lastButOne :: [a] -> a
lastButOne xs 
    | null xs = error "empty list"
    | length xs == 1 = error "list too short"
    | length xs == 2 = head xs
    | otherwise = lastButOne (tail xs)

更实用的解决方案是对函数类型中的偏好进行编码并返回Maybe a,这样如果输入列表太短,就可以返回Nothing

lastButOne :: [a] -> Maybe a
lastButOne xs 
    | null xs = Nothing
    | length xs == 1 = Nothing
    | length xs == 2 = Just (head xs)
    | otherwise = lastButOne (tail xs)

最后,更好的解决方案是使用模式匹配而不是保护长度:

lastButOne :: [a] -> Maybe a
lastButOne [] = Nothing
lastButOne [_] = Nothing
lastButOne [x,_] = Just x
lastButOne (_:xs) = lastButOne xs

答案 1 :(得分:3)

这里有多个错误。

首先,调用语法绑定到左侧,这意味着lastButOne head xs表示“使用两个参数调用lastButOne headxs”,而不是“调用” lastButOne,其结果是headxs一起调用。

其次,你的函数返回一个列表,即使你的描述说它应该返回一个元素。

第三,调用lastButOne (head xs)也是类型错误,因为head返回单个元素,但lastButOne需要列表。我相信你只想在这里写head xs

但是你得到了第四个错误,即函数的前两个分支实际返回整个列表,如果你想返回一个元素,它们就不能。

你还需要考虑应该返回的函数,当它不是至少两个元素长时。应该崩溃,如head那样吗?或者,您是否应该将结果类型更改为Maybe a并返回Nothing

答案 2 :(得分:0)

我是Haskell的新手,但这是我做的解决方案:

lastButOne xs = if null xs || length xs == 1
            then []
            else
                if length xs == 2
                then head xs
                else lastButOne (tail xs)