【问题标题】:Ignore type declaration until last recursion在最后一次递归之前忽略类型声明
【发布时间】:2019-08-08 00:42:43
【问题描述】:

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

函数 decode 应该接受一个元组列表,然后返回一个字符串。

示例

> 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))
   |

【问题讨论】:

  • 这有什么问题? let x = [('h',7),('s',3),('g',1)] in concat [replicate n c | (c, n) <- x].
  • 为什么要折叠? (snd (head x)) 是什么东西? decode ((c, n) : xs) = replicate n c ++ decode xs 对我来说似乎更简单。
  • 是的:几乎总是,模式匹配比fst/snd 等更可取,特别是head 和tail 是evil。
  • 如果你真的想要深奥,那么decode = concatMap (uncurry (flip replicate))。
  • @Ali (++) 的问题发生在您重复将短字符串附加到较长字符串时。在这里,您正在做相反的事情:在短字符串的末尾添加一个长字符串。

标签: haskell recursion types tuples


【解决方案1】:

在 Haskell 中,我们不会抑制错误,我们会修复它们。解决问题的最少编辑是:

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

因此,在 cmets 中提出的许多其他可能性中,

decode :: [(Char,Int)] -> String
decode xs = [ c | (c,i) <- xs, _ <- [1..i]]

【讨论】:

  • 您的第一行适用于所有语言,事实上适用于所有生命。
【解决方案2】:

您的代码的问题在于 : cons 函数。它的类型是a -&gt; [a] -&gt; [a],这意味着它将一个 single 元素放在列表的开头。在您的情况下,您正在尝试将 list(复制的元素)添加到列表中,这就是 ++ 起作用的原因(它的类型是 [a] -&gt; [a] -&gt; [a])。没有办法简单地“忽略类型”,因为类型与 haskell 编译/运行的方式交错,这是一件好事,在这种情况下,编译器将您从其他语言中的“类型不匹配”运行时错误中拯救出来。

如果你想用:写它,那么你不能使用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 倍

【讨论】:

  • 在 StackOverflow 上的第一篇文章真是太棒了。欢迎!
猜你喜欢
  • 1970-01-01
  • 2017-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-21
  • 1970-01-01
  • 2017-03-07
  • 1970-01-01
相关资源
最近更新 更多