Nov. 10th, 2011

deniok: (Default)
В Haskell нельзя перегрузить функцию с одним именем, но разным числом аргументов (например, потому что вывод типов спасует при частичном применении). Поэтому мы были вынуждены в некоторых ситуациях генерировать семейства практически идентичных функций, например zipWith_n
*Test> let x1s = [1,2,3]
*Test> let x2s = [4,5,6]
*Test> let x3s = [7,8,9]
*Test> let x4s = [10,11,12]
*Test> zipWith (\a b -> 2*a+3*b) x1s x2s
[14,19,24]
*Test> zipWith3 (\a b c -> 2*a+3*b+5*c) x1s x2s x3s
[49,59,69]
*Test> zipWith4 (\a b c d -> 2*a+3*b+5*c-4*d) x1s x2s x3s x4s
[9,15,21]
Аппликативные функторы позволяют решить эту проблему
*Test> getZipList $ (\a b -> 2*a+3*b) <$> ZipList x1s <*> ZipList x2s
[14,19,24]
*Test> getZipList $ (\a b c -> 2*a+3*b+5*c) <$> ZipList x1s <*> ZipList x2s <*> ZipList x3s
[49,59,69]
*Test> getZipList $ (\a b c d -> 2*a+3*b+5*c-4*d) <$> ZipList x1s <*> ZipList x2s <*> ZipList x3s <*> ZipList x4s
[9,15,21]
Это, конечно, выглядит кошмарно. Однако упаковка/разупаковка getZipList / ZipList тут нужна, поскольку главным инстансом аппликативного функтора для списков был выбран не зипующий (<<номер в номер>>), а <<каждый с каждым>>. Если нам часто нужно делать такие многоаргументные зипы, то можно определить операторы - аналоги (<*>) и (<$>), но с дополнительной ZipList / getZipList нагрузкой:
infixl 4 >*<, >$<

(>*<) :: [a -> b] -> [a] -> [b]
fs >*< xs = getZipList $ ZipList fs <*> ZipList xs

(>$<) :: (a -> b) -> [a] -> [b]
f  >$< xs = getZipList $ f <$> ZipList xs
После этого всё выглядит вполне компактно и не требует лишних имён для зипов:
*Test> (\a b -> 2*a+3*b) >$< x1s >*< x2s
[14,19,24]
*Test> (\a b c -> 2*a+3*b+5*c) >$< x1s >*< x2s >*< x3s
[49,59,69]
*Test> (\a b c d -> 2*a+3*b+5*c-4*d) >$< x1s >*< x2s >*< x3s >*< x4s
[9,15,21]

Profile

deniok: (Default)
deniok

February 2022

S M T W T F S
  12345
6789101112
13141516171819
20212223 242526
2728     

Style Credit

Expand Cut Tags

No cut tags
Page generated Jun. 20th, 2025 06:00 pm
Powered by Dreamwidth Studios