忽略类型声明,直到上一次递归

时间:2019-03-17 14:08:45

标签: haskell recursion types tuples

我正在尝试编写一个函数,该函数将元组中的数据转换为用于运行长度编码的程序的字符串。我以前曾使用append编写过它,但我一直在尝试对其进行改进。

该函数的解码应该获取一个元组列表,然后返回一个字符串。

示例

> decode [('h',7),('s',3),('g',1)]
"hhhhhhhsssg"
> decode [('z',9),('z',1)]
"zzzzzzzzzz"

我最初是使用append函数以递归方式编写的,该函数可以正常运行,但不是最佳选择,我目前的实现看起来像这样:

decode :: [(Char,Int)] -> String
decode [] = []
decode x = concat(replicate (snd (head x)) (fst (head x)) : decode (tail x)

这会给我一个编译错误,因为decode (tail x)部分不适合我不能更改的类型声明。我确定这是一种不好的做法,但是是否有一种方法可以让程序在完成递归之前不符合类型声明?

    * Couldn't match type `Char' with `[Char]'
      Expected type: [[Char]]
        Actual type: String
    * In the second argument of `(:)', namely `decode (tail x)'
      In the first argument of `concat', namely
        `(replicate (snd (head x)) (fst (head x)) : decode (tail x))'
      In the expression:
        concat (replicate (snd (head x)) (fst (head x)) : decode (tail x))
   |
35 | decode x = concat(replicate (snd (head x)) (fst (head x)) : decode (tail x))
   |

2 个答案:

答案 0 :(得分:5)

您的代码存在问题,是:缺点函数。它的类型是a -> [a] -> [a],这意味着它将单个元素放在列表的开头。在您的情况下,您试图将 list (复制的元素)添加到列表中,这就是++有效(类型为[a] -> [a] -> [a])的原因。无法简单地“忽略类型”,因为类型与haskell编译/运行方式交织在一起,这是一件好事,在这种情况下,编译器使您免于其他lang中的“类型不匹配”运行时错误。

如果您想使用:编写代码,那么就不能使用replicate,则需要创建一个辅助的递归函数来重复char,然后将其解码为零。列表:

decodeA :: [(Char,Int)] -> String
decodeA [] = []
decodeA ((c,n):xs) = rep c n
           where rep ch 0 = decodeA xs
                 rep ch m = ch : (rep ch (m-1))

现在,使用++产生了一个更清晰的解决方案:

decodeB :: [(Char,Int)] -> String
decodeB [] = []
decodeB ((c,n):xs) = replicate n c ++ decodeB xs

并对两种解决方案进行基准测试,第二种方法不仅更清晰,而且速度更快:

基准代码

t1 = [('h',7),('s',3),('g',1)]
t2 = [('z',9),('z',1)]
t3 = [('a',10000), ('b',10000), ('c',10000),('d',10000), ('e',10000), ('f',10000)]

main = defaultMain [
  bgroup "decode" [ bench "t1 decodeA" $ nf decodeA t1
                  , bench "t2 decodeA" $ nf decodeA t2
                  , bench "t3 decodeA" $ nf decodeA t3
                  , bench "t1 decodeB" $ nf decodeB t1
                  , bench "t2 decodeB" $ nf decodeB t2
                  , bench "t3 decodeB" $ nf decodeB t3
                   ]

基准测试结果

benchmarking decode/t1 decodeA
time                 7.152 μs   (7.093 μs .. 7.225 μs)
                     0.999 R²   (0.998 R² .. 1.000 R²)
mean                 7.129 μs   (7.091 μs .. 7.216 μs)
std dev              190.6 ns   (69.72 ns .. 354.5 ns)
variance introduced by outliers: 31% (moderately inflated)

benchmarking decode/t2 decodeA
time                 6.283 μs   (6.235 μs .. 6.340 μs)
                     0.999 R²   (0.999 R² .. 1.000 R²)
mean                 6.268 μs   (6.239 μs .. 6.326 μs)
std dev              137.8 ns   (71.41 ns .. 211.7 ns)
variance introduced by outliers: 24% (moderately inflated)

benchmarking decode/t3 decodeA
time                 32.67 ms   (32.31 ms .. 33.08 ms)
                     0.999 R²   (0.998 R² .. 1.000 R²)
mean                 32.68 ms   (32.53 ms .. 32.93 ms)
std dev              406.7 μs   (238.0 μs .. 613.5 μs)

benchmarking decode/t1 decodeB
time                 1.208 μs   (1.199 μs .. 1.220 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)
mean                 1.212 μs   (1.204 μs .. 1.228 μs)
std dev              34.30 ns   (19.59 ns .. 62.18 ns)
variance introduced by outliers: 38% (moderately inflated)

benchmarking decode/t2 decodeB
time                 923.6 ns   (916.9 ns .. 931.6 ns)
                     0.999 R²   (0.997 R² .. 1.000 R²)
mean                 923.8 ns   (917.0 ns .. 950.3 ns)
std dev              38.01 ns   (9.440 ns .. 84.90 ns)
variance introduced by outliers: 57% (severely inflated)

benchmarking decode/t3 decodeB
time                 1.250 ms   (1.229 ms .. 1.274 ms)
                     0.997 R²   (0.995 R² .. 0.999 R²)
mean                 1.248 ms   (1.239 ms .. 1.269 ms)
std dev              47.55 μs   (32.05 μs .. 78.69 μs)
variance introduced by outliers: 26% (moderately inflated)

在这种情况下,decodeB比最大测试用例的decodeA快32倍

答案 1 :(得分:2)

在Haskell中,我们不排除错误,而是对其进行修复。解决该问题的最小编辑为:

p.join()

当然是decode :: [(Char,Int)] -> String decode [] = [] decode x = -- concat(replicate (snd (head x)) (fst (head x)) : decode (tail x)) -- BAD = concat[replicate (snd (head x)) (fst (head x)) , decode (tail x)] -- OK = concat(replicate (snd (head x)) (fst (head x)) : [decode (tail x)]) -- OK ,因此

concat [a,b] == a ++ b

         =           replicate (snd (head x)) (fst (head x)) ++ decode (tail x)

因此,在注释中建议的许多其他可能性中,

decode ((c,i):xs) =  replicate i c ++ decode xs