Haskell 忽略类型声明直到最后一次递归

Haskell 忽略类型声明直到最后一次递归,haskell,recursion,types,tuples,Haskell,Recursion,Types,Tuples,我试图编写一个函数,将元组中的数据转换成字符串,用于运行长度编码的程序。我以前使用append编写过它,但我一直在努力改进它 函数decode应该获取一个元组列表,然后返回一个字符串 示例 > decode [('h',7),('s',3),('g',1)] "hhhhhhhsssg" > decode [('z',9),('z',1)] "zzzzzzzzzz" 我最初使用append函数递归编写它,该函数运行良好,但不是最佳的,我当前的实现如下所示: decode :: [(C

我试图编写一个函数,将元组中的数据转换成字符串,用于运行长度编码的程序。我以前使用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))
   |

在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)
i、 e

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

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

decode xs=[c |(c,i)您的代码的问题在于
cons函数。它的类型是
a->[a]->[a]
,这意味着它将单个元素放在列表的开头。在您的情况下,您试图将列表(复制的元素)添加到列表中,这就是
+
起作用的原因(它的类型是
[a]->[a]>[a] 
)。没有办法简单地“忽略类型”,因为类型与haskell编译/运行的方式交织在一起,这是一件好事,在这种情况下,编译器使您避免了其他语言中的“类型不匹配”运行时错误

如果要使用
编写它,则不能使用
replicate
,您需要创建一个辅助递归函数来重复字符,并在零上解码列表的其余部分:

decodeA::[(字符,Int)]->字符串
解码a[]=[]
decodeA((c,n):xs)=代表c n
其中rep ch 0=解码一个xs
代表ch m=ch:(代表ch(m-1))
现在,使用
++
可以得到一个更清晰的解决方案:

decodeB::[(字符,Int)]->字符串
decodeB[]=[]
decodeB((c,n):xs)=复制nc++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[
b组“解码”[bench“t1 decodeA”$nf decodeA t1
,台架“t2 decodeA”$nf decodeA t2
,台架“t3解码A”$nf解码A t3
,台架“t1解码B”$nf解码B t1
,台架“t2 decodeB”$nf decodeB t2
,台架“t3解码B”$nf解码B t3
]
基准结果

基准测试解码/t1解码a
时间7.152μs(7.093μs..7.225μs)
0.999平方米(0.998平方米..1.000平方米)
平均7.129μs(7.091μs..7.216μs)
标准偏差190.6纳秒(69.72纳秒..354.5纳秒)
异常值引入的方差:31%(适度膨胀)
基准测试解码/t2解码a
时间6.283μs(6.235μs..6.340μs)
0.999平方米(0.999平方米..1.000平方米)
平均6.268μs(6.239μs..6.326μs)
标准偏差137.8纳秒(71.41纳秒..211.7纳秒)
异常值引入的方差:24%(适度膨胀)
基准测试解码/t3解码a
时间32.67毫秒(32.31毫秒..33.08毫秒)
0.999平方米(0.998平方米..1.000平方米)
平均32.68毫秒(32.53毫秒..32.93毫秒)
标准偏差406.7μs(238.0μs..613.5μs)
基准测试解码/t1解码b
时间1.208μs(1.199μs..1.220μs)
1.000平方米(0.999平方米..1.000平方米)
平均1.212μs(1.204μs..1.228μs)
标准偏差34.30纳秒(19.59纳秒..62.18纳秒)
异常值引入的方差:38%(适度膨胀)
基准测试解码/t2解码b
时间923.6纳秒(916.9纳秒..931.6纳秒)
0.999平方米(0.997平方米..1.000平方米)
平均923.8纳秒(917.0纳秒..950.3纳秒)
标准偏差38.01纳秒(9.440纳秒..84.90纳秒)
异常值引入的方差:57%(严重夸大)
基准测试解码/t3解码b
时间1.250毫秒(1.229毫秒..1.274毫秒)
0.997平方米(0.995平方米..0.999平方米)
平均1.248毫秒(1.239毫秒..1.269毫秒)
标准偏差47.55μs(32.05μs..78.69μs)
异常值引入的方差:26%(适度膨胀)

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

这有什么不对?
让x=[('h',7),('s',3),('g',1)]在concat[replicate n c |(c,n)为什么折叠?那么
(snd(head x))
东西是什么呢?
解码((c,n):xs=replicate n c++decode xs对我来说似乎简单得多。是的:几乎总是,模式匹配优于
fst
/
snd
等,尤其是
head
tail
是。如果你真的想深奥,那么
decode=concatMap(uncurry(flip replicate))
@Ali当你反复将一个短字符串附加到一个长字符串后,
(++)
的问题就会出现。在这里,你做的是相反的:在一个短字符串的末尾添加一个长字符串。你的第一行适用于所有语言,事实上适用于所有语言。这是StackOverflow的第一篇好文章。欢迎!
decode :: [(Char,Int)] -> String
decode xs = [ c | (c,i) <- xs, _ <- [1..i]]