Для Haskell мне нравится
let s n = sum [0,n..999] in s 3 + s 5 - s 15
или
sum $ filter ((>1).(gcd 15)) [0..999]
Для забавы версия Рубе-Голдберга:
import Data.Bits
sum $ zipWith (*) [1..999] $ zipWith (.|.) (cycle [0,0,1]) (cycle [0,0,0,0,1])
Хорошо, время объяснения,
Первая версия определяет небольшую функцию s, которая суммирует все кратные n до 999. Если мы сложим все кратные 3 и все кратные 5, мы включили все кратные 15 дважды (один раз в каждом списке), следовательно, нам нужно вычесть их один раз.
Во второй версии используется тот факт, что 3 и 5 - простые числа.Если число содержит один или оба из факторов 3 и 5, то значение gcd этого числа и 15 будет равно 3, 5 или 15, поэтому в каждом случае значение gcd будет больше единицы.Для других чисел без общего множителя с 15 gcd становится равным 1. Это хороший прием для проверки обоих условий за один шаг.Но будьте осторожны, он не будет работать для произвольных чисел, например, когда у нас было 4 и 9, тест gdc x 36 > 1
не будет работать, как gcd 6 36 == 6
, но ни mod 6 4 == 0
, ни mod 6 9 == 0
.
Третья версия довольно забавная.cycle
повторяет список снова и снова.cycle [0,0,1]
кодирует «шаблон делимости» для 3, а cycle [0,0,0,0,1]
делает то же самое для 5. Затем мы «или» оба списка вместе, используя zipWith
, что дает нам [0,0,1,0,1,1,0,0,1,1,0,1...]
.Теперь мы снова используем zipWith
, чтобы умножить это на фактические числа, в результате чего [0,0,3,0,5,6,0,0,9,10,0,12...]
.Тогда мы просто добавим это.
Знание различных способов сделать то же самое может быть расточительным для других языков, но для Хаскелла это важно.Вам нужно выявлять закономерности, подбирать трюки и идиомы и много играть, чтобы обрести умственную гибкость для эффективного использования этого языка.Задачи, такие как проект Эйлера, являются хорошей возможностью для этого.