Haskell 用另一个值替换列表中的值

RNe*_*Nee 0 haskell

我对 Haskell 很陌生。我正在尝试编写一个程序,该程序采用两个值和一个列表,并将列表中第一个值的每个实例替换为第二个。例如repOcc 'n' 'i' "pink"会返回"piik"

以下是我的代码:

repOcc :: t -> t -> [t] -> [t]
repOcc x y (z:zs) = if z == x
                      then z = y
                      subst x y zs
                      else subst x y zs
Run Code Online (Sandbox Code Playgroud)

我在编译时收到的错误是:

rev.hs:3 :32: error: 
   parse error on input '='
   Perhaps you need a 'let' in a 'do' block?
   e.g. 'let x = 5' instead of 'x = 5'
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)

Wil*_*sem 5

您的程序看起来相当“命令式”,而 Haskell 的目标是更加“声明式”。所以你不能z在列表中设置变量:一旦列表被构造,你就不能再改变它。因此,您必须构建一个新列表,其中将等于 的元素x设置为y

接下来,您将使用该(==)功能。该函数在Eq类型类中定义,因此您需要将Eq t类型约束添加到签名中。

所以现在我们可以开始构造这样的函数了。通常在处理列表时,我们使用递归。递归的基本情况通常是空列表。如果我们遇到空列表,我们应该返回一个空列表,不管是什么xy是什么。所以我们使用下划线作为“ don't care ”模式,并[]作为列表模式使用,并写成:

repOcc _ _ [] = []
Run Code Online (Sandbox Code Playgroud)

递归情况是当列表在模式中包含头h和尾t(h:t)。在这种情况下,我们检查是否h等于x。如果是,我们构造一个带有y头部的列表,否则h仍然是头部。

repOcc x y (h:t) | x == h = y : tl
                 | otherwise = h : tl
Run Code Online (Sandbox Code Playgroud)

现在的问题仍然是结果列表的尾部tl应该是什么。这里我们使用递归,所以我们称之为repOccx y t

    where tl = repOcc x y t
Run Code Online (Sandbox Code Playgroud)

或者把它放在一起:

repOcc :: Eq t => t -> t -> [t] -> [t]
repOcc _ _ [] = []
repOcc x y (h:t) | x == h = y : tl
                 | otherwise = h : tl
    where tl = repOcc x y t
Run Code Online (Sandbox Code Playgroud)

我们可以编写这样的递归函数,但上面实际上是map函数的一个特例:我们映射每个字符,检查它是否等于x,如果等于,则返回y,否则返回h。所以我们可以将上面的改写为:

repOcc :: Eq t => t -> t -> [t] -> [t]
repOcc x y ls = map (\h -> if h == x then y else h) ls
Run Code Online (Sandbox Code Playgroud)

我们可以通过使用eta -reduction进一步改进代码:

repOcc :: Eq t => t -> t -> [t] -> [t]
repOcc x y = map (\h -> if h == x then y else h)
Run Code Online (Sandbox Code Playgroud)