首页 文章

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

提问于
浏览
-2

我对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

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

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.

1 回答

  • 3

    你的程序看起来很“必要" whereas Haskell aims to be more "声明” . 因此,您无法在列表中设置变量 z :构建列表后,您无法再对其进行更改 . 因此,您必须构造一个新列表,其中等于 x 的元素设置为 y .

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

    所以现在我们可以开始构建这样的功能了 . 通常在使用列表时,我们使用递归 . 递归的基本情况通常是空列表 . 如果我们遇到空列表,我们应该返回一个空列表,无论 xy 是什么 . 所以我们使用下划线作为“不关心”模式,并使用 [] 作为列表模式,并写:

    repOcc _ _ [] = []
    

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

    repOcc x y (h:t) | x == h = y : tl
                     | otherwise = h : tl
    

    现在问题仍然是结果列表的尾部 tl 应该是什么 . 这里我们使用递归,所以我们用 x y t 调用 repOcc

    where tl = repOcc x y t
    

    或者把它放在一起:

    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
    

    我们可以编写这样的递归函数,但上面实际上是 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
    

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

    repOcc :: Eq t => t -> t -> [t] -> [t]
    repOcc x y = map (\h -> if h == x then y else h)
    

相关问题