我不知道 F#,但我会发布一些 Haskell,希望它足够接近以供使用。 (我只有 VS 2005 和一个古老版本的 F#,所以我认为在我的机器上发布一些可以运行的东西会更加混乱)
首先让我发布你的伪代码的 Python 实现,以确保我得到正确的答案:
def convolve(signal, filter):
conv = [0 for _ in range(len(signal) + len(filter) - 1)]
for i in range(len(signal)):
for j in range(len(filter)):
conv[i + j] += signal[i] * filter[-j-1]
return conv
现在convolve([1,1,1], [1,2,3]) 提供[3, 5, 6, 3, 1]。如果这是错误的,请告诉我。
我们可以做的第一件事就是把内循环变成一个zipWith;我们本质上是以一种特殊的方式添加一系列行,在上面的例子中:[[3,2,1], [3,2,1], [3,2,1]]。为了生成每一行,我们将使用反向过滤器压缩 signal 中的每个 i:
makeRow filter i = zipWith (*) (repeat i) (reverse filter)
(注意:根据快速谷歌,zipWith 在 F# 中是 map2。您可能必须使用列表推导而不是 repeat)
现在:
makeRow [1,2,3] 1
=> [3,2,1]
makeRow [1,2,3] 2
=> [6,4,2]
要为所有i 获取此信息,我们需要映射信号:
map (makeRow filter) signal
=> [[3,2,1], [3,2,1], [3,2,1]]
很好。现在我们只需要一种正确组合行的方法。我们可以通过注意组合将新行添加到现有数组中来做到这一点,除了第一个元素,它卡在前面。例如:
[[3,2,1], [6,4,2]] = 3 : [2 + 6, 1 + 4] ++ [2]
// or in F#
[[3; 2; 1]; [6; 4; 2]] = 3 :: [2 + 6; 1 + 4] @ [2]
所以我们只需要编写一些在一般情况下执行此操作的代码:
combine (front:combinable) rest =
let (combinable',previous) = splitAt (length combinable) rest in
front : zipWith (+) combinable combinable' ++ previous
现在我们有了生成所有行的方法以及将新行与现有数组组合的方法,我们所要做的就是通过折叠将两者结合在一起:
convolve signal filter = foldr1 combine (map (makeRow filter) signal)
convolve [1,1,1] [1,2,3]
=> [3,5,6,3,1]
所以这是一个功能版本。我认为这是相当清楚的,只要您了解foldr 和zipWith。但它至少与命令式版本一样长,就像其他评论者所说的那样,在 F# 中可能效率较低。这就是一个地方的全部内容。
makeRow filter i = zipWith (*) (repeat i) (reverse filter)
combine (front:combinable) rest =
front : zipWith (+) combinable combinable' ++ previous
where (combinable',previous) = splitAt (length combinable) rest
convolve signal filter = foldr1 combine (map (makeRow filter) signal)
编辑:
正如所承诺的,这是一个 F# 版本。这是在 VS2005 上使用非常古老的版本(1.9.2.9)编写的,所以要小心。另外我在标准库中找不到splitAt,但是我不太了解F#。
open List
let gen value = map (fun _ -> value)
let splitAt n l =
let rec splitter n l acc =
match n,l with
| 0,_ -> rev acc,l
| _,[] -> rev acc,[]
| n,x::xs -> splitter (n - 1) xs (x :: acc)
splitter n l []
let makeRow filter i = map2 ( * ) (gen i filter) (rev filter)
let combine (front::combinable) rest =
let combinable',previous = splitAt (length combinable) rest
front :: map2 (+) combinable combinable' @ previous
let convolve signal filter =
fold1_right combine (map (makeRow filter) signal)