初学者haskell代码是完全错误的

初学者haskell代码是完全错误的,haskell,Haskell,我对哈斯克尔真的很陌生。尝试用递归实现一些非常简单的东西。返回列表中第一个匹配值的索引的函数。下面的代码不起作用,不知道为什么,有人知道我如何更正此代码吗 linear_search :: (Eq a) => a -> [a] -> a linear_search b w = linear_search_tail 0 b w where linear_search_tail :: (Eq a) => a -> a -> [a] -> a

我对哈斯克尔真的很陌生。尝试用递归实现一些非常简单的东西。返回列表中第一个匹配值的索引的函数。下面的代码不起作用,不知道为什么,有人知道我如何更正此代码吗

linear_search :: (Eq a) => a -> [a] -> a
linear_search b w = linear_search_tail 0 b w
  where linear_search_tail :: (Eq a) => a -> a -> [a] -> a
        linear_search_tail x y [] = -1
        linear_search_tail x y (z:zs)
          | y == z = x
          | otherwise = linear_search_tail (x+1) y zs

main :: IO ()
main = print (linear_search 1 [2,5,4,3,7,3,1,2,3,4,5,7,8])
编辑:开始工作了。这是正确的代码:

#!/usr/bin/env runghc

linear_search :: (Eq a) => a -> [a] -> Int
linear_search b w = linear_search_tail 0 b w
  where linear_search_tail :: (Eq a) => Int -> a -> [a] -> Int 
        linear_search_tail x y [] = -1
        linear_search_tail x y (z:zs)
              | y == z = x
              | otherwise = linear_search_tail (x+1) y zs

main :: IO ()
main = print (linear_search 1 [2,5,4,3,7,3,1,2,3,4,5,7,8])

代码正在搜索一个数字的索引,这意味着is应该返回一个
Int

linear_search :: (Eq a) => a -> [a] -> Int
还有一个类似的问题,看看你能不能找到

linear\u search\u tail::(等式a)=>Int->a->[a]->Int


代码正在搜索一个数字的索引,这意味着is应该返回一个
Int

linear_search :: (Eq a) => a -> [a] -> Int
还有一个类似的问题,看看你能不能找到

linear\u search\u tail::(等式a)=>Int->a->[a]->Int


让我对更正后的代码添加一些注释:

linear_search :: (Eq a) => a -> [a] -> Int
linear_search b w = linear_search_tail 0 b w
  where linear_search_tail :: (Eq a) => Int -> a -> [a] -> Int 
        linear_search_tail x y [] = -1
        linear_search_tail x y (z:zs)
              | y == z = x
              | otherwise = linear_search_tail (x+1) y zs
在辅助函数中,第二个参数
y
的传递没有变化,因此它始终等于
b
。您可以删除该参数并直接使用该参数:

linear_search :: (Eq a) => a -> [a] -> Int
linear_search b w = linear_search_tail 0 w
  where -- linear_search_tail :: (Eq a) => Int -> [a] -> Int 
        linear_search_tail x [] = -1
        linear_search_tail x (z:zs)
              | b == z    = x
              | otherwise = linear_search_tail (x+1) zs
(在这里,我必须删除/注释内部类型签名,因为Haskell将内部
a
类型变量解释为独立于外部
a
——您可以通过启用扩展声明它们是相同的,但让我们保持简单,只需省略签名即可。)

上述函数在出现错误时返回
-1
。这在Haskell中并不惯用,在Haskell中更常见的是返回一个
Maybe Int
,这是一种包含表示整数
的值的类型,仅0、仅1、仅2、
加上一个可解释为“无结果”的不同值
Nothing
。如果我们使用它,我们会得到:

linear_search :: (Eq a) => a -> [a] -> Maybe Int
linear_search b w = linear_search_tail 0 w
  where linear_search_tail x [] = Nothing
        linear_search_tail x (z:zs)
              | b == z    = Just x
              | otherwise = linear_search_tail (x+1) zs
请注意,这将使调用者的生活变得不那么方便,调用者现在将其索引包装在
Just
框中,或者
Nothing
中。这实际上是一件好事,因为现在调用者不能轻易忘记检查“notfound”情况,并使用一个普通整数,假装它不是
-1
。调用者现在被迫使用以下命令

case linear_search 1 [2,3,4,5] of
   Just x  -> "Found at position " ++ x
   Nothing -> "Not found!"
最后,请注意第一行

linear_search b w = linear_search_tail 0 w
也可以写成

linear_search b = linear_search_tail 0
也就是说,可以在定义的尾部位置省略相同的变量。这称为eta减少,并不一定会使代码看起来更好。在这种情况下,这没有多大帮助。我只是提到它,因为将来可能会看到类似的代码


在学习了更多Haskell之后,您可能想知道如何利用库函数来实现这一点。例如,
Data.List
中的
elemIndex
库函数执行您试图编写的线性搜索。或者

linear_search :: (Eq a) => a -> [a] -> Maybe Int
linear_search x xs = case filter ((x ==) . snd) $ zip [0..] xs of
                     []       -> Nothing
                     (i, _):_ -> Just i

你现在可能觉得这本书不可读,但你可以继续学习,等你更熟悉Haskell的时候再回来。同时,像您那样编写递归函数是正确的方法。

让我对更正后的代码添加一些注释:

linear_search :: (Eq a) => a -> [a] -> Int
linear_search b w = linear_search_tail 0 b w
  where linear_search_tail :: (Eq a) => Int -> a -> [a] -> Int 
        linear_search_tail x y [] = -1
        linear_search_tail x y (z:zs)
              | y == z = x
              | otherwise = linear_search_tail (x+1) y zs
在辅助函数中,第二个参数
y
的传递没有变化,因此它始终等于
b
。您可以删除该参数并直接使用该参数:

linear_search :: (Eq a) => a -> [a] -> Int
linear_search b w = linear_search_tail 0 w
  where -- linear_search_tail :: (Eq a) => Int -> [a] -> Int 
        linear_search_tail x [] = -1
        linear_search_tail x (z:zs)
              | b == z    = x
              | otherwise = linear_search_tail (x+1) zs
(在这里,我必须删除/注释内部类型签名,因为Haskell将内部
a
类型变量解释为独立于外部
a
——您可以通过启用扩展声明它们是相同的,但让我们保持简单,只需省略签名即可。)

上述函数在出现错误时返回
-1
。这在Haskell中并不惯用,在Haskell中更常见的是返回一个
Maybe Int
,这是一种包含表示整数
的值的类型,仅0、仅1、仅2、
加上一个可解释为“无结果”的不同值
Nothing
。如果我们使用它,我们会得到:

linear_search :: (Eq a) => a -> [a] -> Maybe Int
linear_search b w = linear_search_tail 0 w
  where linear_search_tail x [] = Nothing
        linear_search_tail x (z:zs)
              | b == z    = Just x
              | otherwise = linear_search_tail (x+1) zs
请注意,这将使调用者的生活变得不那么方便,调用者现在将其索引包装在
Just
框中,或者
Nothing
中。这实际上是一件好事,因为现在调用者不能轻易忘记检查“notfound”情况,并使用一个普通整数,假装它不是
-1
。调用者现在被迫使用以下命令

case linear_search 1 [2,3,4,5] of
   Just x  -> "Found at position " ++ x
   Nothing -> "Not found!"
最后,请注意第一行

linear_search b w = linear_search_tail 0 w
也可以写成

linear_search b = linear_search_tail 0
也就是说,可以在定义的尾部位置省略相同的变量。这称为eta减少,并不一定会使代码看起来更好。在这种情况下,这没有多大帮助。我只是提到它,因为将来可能会看到类似的代码


在学习了更多Haskell之后,您可能想知道如何利用库函数来实现这一点。例如,
Data.List
中的
elemIndex
库函数执行您试图编写的线性搜索。或者

linear_search :: (Eq a) => a -> [a] -> Maybe Int
linear_search x xs = case filter ((x ==) . snd) $ zip [0..] xs of
                     []       -> Nothing
                     (i, _):_ -> Just i

你现在可能觉得这本书不可读,但你可以继续学习,等你更熟悉Haskell的时候再回来。同时,像您这样编写递归函数是正确的方法。

@paxdiablo它使用尾部递归,这意味着没有堆栈溢出和双关语。没有双关语。如果你包含错误信息,对潜在的回答者很有帮助@paxdiablo在Haskell中计算此函数时使用常量空间,而不会破坏堆栈。相同函数调用的Haskell标准库实现。@paxdiablo好吧,因为我们不能使用递归以外的任何东西,我想我们被卡住了。@paxdiablo Haskell中没有调用堆栈。@paxdiablo使用尾部递归,这意味着没有堆栈溢出和双关语。没有双关语。如果你包含错误信息,对潜在的回答者很有帮助@paxdiablo在Haskell中计算此函数时使用常量空间,而不会破坏堆栈。Haskell标准库实现了相同的函数调用。@paxdiablo好吧,因为我们不能使用递归以外的任何东西,我想我们是