Задачи из project euler

79 views
Skip to first unread message

Ivan Samsonov

unread,
Feb 28, 2015, 9:45:11 AM2/28/15
to clojure...@googlegroups.com
Надеюсь, группа жива.

Преамбула

Время от времени, чтобы "не загрубеть" от технологий, которые ты уже знаешь более-менее, я изучаю другие языки программирования.
Технология изучения у меня простая: читаю примерно половину книги по языку, чтобы понять основы, далее перехожу к практике. 
В качестве практики я прорешиваю самые простые задачи на codeforces и project-euler (далее PE), в случае с clojure первый ресурс, к сожалению, не подходит.

К делу

Наверное, у каждого языка можно выделить некоторые правила, как на нем писать надо, а как не надо, так называемое %language%-way
Например: в Ruby, в 90% случаев лучше использовать итераторы вместо циклов, а без цикла for вы совершенно точно сможете обойтись.
Сейчас подобную картину я пытаюсь уложить в голове по поводу clojure.
С циклами тут все туго, да еще и новый функциональный подход... Разобраться в erlang и ocaml (кроме типов), признаюсь, мне было проще.
Ниже я приведу ссылки на решения и некоторые свои комментарии. Если вы видите как можно было решить лучше именно с точки зрения
языка, а не алгоритма в целом (так как я ставлю своей задачей научится программировать на языке, а не написать самое быстрое решение), то
буду рад услышать комментарии.

Почитав про язык, я подумал, что очень хорошо использовать ленивые последовательности везде, где это возможно и, в результате, получилось:
(Просуммировать все делимые 3 или 5 ниже 1000)
Я вижу этот код так: делаем ленивую последовательность и добавляем в нее числа пока очередное число не превысит 1000, после этого суммируем.
Правильно ли я понимаю, что после суммирования последовательность соберется сборщиком?

(Просуммировать все четные числа фибоначчи до миллиона)

(Найти самый большой простой делитель числа)
Правильно ли я понимаю, что тут будет хвостовая рекурсия?

(Найти больший палиндром среди произведения всех трехзначных чисел)
Нормально ли использовать так цикл for? Можно ли написать более оптимально функцию palindrom?
Еще одна задача, где я использовал for: https://github.com/kronos/project-euler/blob/master/9/9.clj
Условие тут: https://projecteuler.net/problem=9

Найти разность между квадратом суммы и суммой квадратов
Ну тут уж совсем просто, можно ли еще проще?

Самая длинная по коду задача, скажем, суть ее заключается в умении переводить из римских в арабские и наоборот:
Тут совершенно точно, можно сделать лучше. Особенно мне не нравится строчка "(> arabic 999) (str (apply str (repeat (quot arabic 1000) "M")) (to-roman (rem arabic 1000)))".
С удовольствием послушаю ваши идеи, как сделать код более clojure-way.
Спасибо!

Eduard Bondarenko

unread,
Mar 3, 2015, 3:09:27 AM3/3/15
to clojure...@googlegroups.com
Привет,

Многие функции (filter, map, take-while etc) ленивые, не нужно
дополнительно их заворачивать в lazy-seq, lazy-cat
Например:

(let [x #(or (zero? (rem % 3)) (zero? (rem % 5)))]
(->> (iterate inc 3)
(filter x)
(take-while (partial > 1000))
(apply +)))

iterate генерирует бесконечную последовательность.
Если бы filter был не ленивый, то результат пришлось бы долго ждать.
> --
> Вы получили это сообщение, поскольку подписаны на группу "Clojure Russian".
> Чтобы отменить подписку на эту группу и больше не получать от нее сообщения,
> отправьте письмо на электронный адрес
> clojure-russi...@googlegroups.com.
> Чтобы настроить другие параметры, перейдите по ссылке
> https://groups.google.com/d/optout.

Eduard Bondarenko

unread,
Mar 3, 2015, 3:19:46 AM3/3/15
to clojure...@googlegroups.com
2015-02-28 16:45 GMT+02:00 Ivan Samsonov <hro...@gmail.com>:
Да, все собирается что не используется.

>
> (Просуммировать все четные числа фибоначчи до миллиона)
> https://github.com/kronos/project-euler/blob/master/2/2.clj
>
> (Найти самый большой простой делитель числа)
> https://github.com/kronos/project-euler/blob/master/3/3.clj
> Правильно ли я понимаю, что тут будет хвостовая рекурсия?

Мне кажется тут лучше использовать loop/recur

(loop [i 0 sum 0]
(if (< i 100)
(recur (inc i) (+ i sum))
sum))

>
> (Найти больший палиндром среди произведения всех трехзначных чисел)
> https://github.com/kronos/project-euler/blob/master/4/4.clj
> Нормально ли использовать так цикл for? Можно ли написать более оптимально
> функцию palindrom?

Нормально, можно поискать более быстрый способ чем перебор, но код
выглядит хорошо.
Например начать с верхней границы и найти только первый.

> Еще одна задача, где я использовал for:
> https://github.com/kronos/project-euler/blob/master/9/9.clj
> Условие тут: https://projecteuler.net/problem=9
>
> Найти разность между квадратом суммы и суммой квадратов
> https://github.com/kronos/project-euler/blob/master/6/6.clj
> Ну тут уж совсем просто, можно ли еще проще?
>
> Самая длинная по коду задача, скажем, суть ее заключается в умении
> переводить из римских в арабские и наоборот:
> https://github.com/kronos/project-euler/blob/master/89/89.clj
> Тут совершенно точно, можно сделать лучше. Особенно мне не нравится строчка
> "(> arabic 999) (str (apply str (repeat (quot arabic 1000) "M")) (to-roman
> (rem arabic 1000)))".
> С удовольствием послушаю ваши идеи, как сделать код более clojure-way.
> Спасибо!
>
Reply all
Reply to author
Forward
0 new messages