Я не знаю 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]]
. Чтобы сгенерировать каждую строку, мы заархивируем каждый i
в signal
с обратным фильтром:
makeRow filter i = zipWith (*) (repeat i) (reverse filter)
(Примечание: согласно быстрому Google, zipWith
- это map2
в F #. Возможно, вам придется использовать понимание списка вместо 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)
Edit:
Как и было обещано, вот версия F #. Это было написано с использованием очень древней версии (1.9.2.9) на VS2005, поэтому будьте осторожны. Также я не смог найти 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)