Pull to refresh

Решаем Project Euler на F#: Задача 1

Reading time2 min
Views1.3K
Прочитав первые несколько статей из цикла Влюбляемся в F#, я и в самом деле, если не влюбился в него, то по меньшей мере заинтересовался. Настолько, что не вытерпел ожидания следующей дозы и решил продолжить изучение самостоятельно.
В процессе луркинга я наткнулся на чрезвычайно увлекательный сайт Project Euler, который на мой взгляд, как нельзя лучше подходит, чтобы постепенно изучить все, или по крайней мере большинство тонкостей F#. Сейчас я предлагаю для начала рассмотреть решение самой первой задачи с этого сайта. Она очень простая, и ее решение думаю не вызовет сложностей даже у тех, кто знаком с F# только по вышеприведенной статье. Итак…

Условие звучит следующим образом:

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.


Решение этой задачи выглядит крайне лаконично:

#light
let _ =
  [1..999]
  |> List.filter (fun x -> x%3=0 or x%5=0)
  |> List.fold_left (+) 0
  |> printfn("The answer is %d")


Думаю, что в целом здесь нет ничего сложного, однако следует обратить внимание на несколько вещей, которые еще не упоминались в цикле статей о F#.
Во-первых, это оператор "|>", который формально определяется следующим образом:
let (|>) a f = f a

а говоря попросту, передает посчитанное перед ним значение в качестве аргумента следующей функции. Этот простой оператор позволяет в многих случаях существенно сократить код.

Во-вторых, здесь применяется лямбда-функция для определения чисел, делящихся на 3 или на 5, уже знакомая даже большинству программистов-императивщиков благодаря LINQ. Синтаксис ее отличается от LINQ разве что наличием fun перед определением функции, и использованием -> вместо =>.

В-третьих, здесь используются две встроенные функции для работы со списками: List.filter и List.fold_left. Первая из них фильтрует список в соответствии с заданной булевой функцией, в качестве которой мы и используем нашу лямбду. Вторая в нашем случае суммирует все элементы списка. Ноль, стоящий вторым аргументом в списке, задает начальное значение аккумулятора. Таким образом, для перемножения всех элементов списка код будет выглядеть так: List.fold_left (*) 1.
Естественно, обе эти функции можно написать и самому, например для фильтрации она может выглядеть как-то так:

let rec filter filter_fun lst =
   match lst with
   |[] -> []
   |head::tail -> if filter_fun head then head::(filter filter_fun tail)
                 else filter filter_fun tail


но думаю, уже все давно пришли к убеждению, что изобретать велосипед стоит далеко не всегда.
Таким образом, вся программа состоит по сути из двух последовательных шагов, которые незамедлительно приносят искомый результат. Довольно красиво, не правда ли?

UPD: Друзья, если вы минусуете топик или карму, прошу, напишите парус строчек, за что. К критике отношусь спокойно, а в следующий раз постараюсь сделать лучше. Спасибо.

Tags:
Hubs:
Total votes 12: ↑9 and ↓3+6
Comments6

Articles