Здравствуйте, Max Mouratov, Вы писали:
MM>Здравствуйте, demi, Вы писали:
D>>Привет, я изучаю OCaml и ФП в частности. Вот функция которая идет по тексту и выкидывает комментарии вида /* ... */. Простенькая задачка
Может как-то покрасивше написать можно? Что хорошо получилось, что — плохо? Есть ощущение что многа букаф) В частности, принимаются замечания по coding style, но с обоснованиями.
MM>Начинаем пинать.
MM>Во-первых, твоя программа не работает -- спотыкается на простейших случаях:
MM>Незакрытый комментарий:
MM>MM># removeComments "/*abc";;
MM>Exception: Invalid_argument "String.sub".
MM>
MM>Вложенные комментарии:
MM>MM># removeComments "aaa/*bbb/*ccc*/ddd*/eee";;
MM>- : string = "aaaddd*/eee"
MM>
MM>Во-вторых, она непонятная -- за деревьями леса не видно. Абстракции очень неэффективные. Похоже на поток мысли задолбавшегося студента. Я бы дал конструктивные предложения к улучшению программы, если бы что-то в ней понял. Предпочёл не тратить время и написал пару своих версий:
MM>
MM> Простой функциональный вариант:
MM>MM>exception Syntax_error of string
MM>let unclosed_comment () =
MM> raise (Syntax_error "Неожиданное закрытие комментария")
MM>(* Удаление вложенных комментариев - - - - - - - - - - - - - - - - - - - - - - *)
MM>let purge_comments str =
MM> let rec skip s i = function
MM> | '*' :: '/' :: rest when i = 0 -> unclosed_comment ()
MM> | '*' :: '/' :: rest -> skip s (i-1) rest
MM> | '/' :: '*' :: rest -> skip s (i+1) rest
MM> | a :: rest when i = 0 -> skip (a :: s) i rest
MM> | a :: rest -> skip s i rest
MM> | [] -> List.rev s in
MM> str |> String.to_list
MM> |> skip [] 0
MM> |> String.of_list
MM>
MM>Вложенные комментарии корректно отрабатываются, ошибочные ситуации локализуются. Обрати внимание что [skip] -- хвосторекурсивная функция и стек на больших строках не кончается.
MM>Проблемы этой программы:
MM>1) Производительность может в некоторых случаях оказаться неприемлимой. Можно увеличить скорость работы, если использовать модуль Buffer из стандартной библиотеки, но это будет выглядеть ещё неприятнее.
MM>2) Это уродский говнокод, тоже очень низкоуровневый. Такой подход плохо масштабируется: пришлось бы много думать при необходимости немного усложнить программу (добавить поддержку ещё одного вида комментариев? поддержку докстрингов?).
MM> Декларативный вариант с PEG парсером на camlp4:
MM>MM>(* Удаление вложенных комментариев - - - - - - - - - - - - - - - - - - - - - - *)
MM>let purge_comments =
MM> <:peg<
MM> somechar : char <- !"/*" !"*/" [^] as c -> c
MM> comment : unit <- nested / somechar
MM> nested : unit <- "/*" comment* "*/"
MM> unclosed : unit <- "/*" comment*
MM> text : string <-
MM> somechar* as s1 nested somechar* as s2 -> String.of_list (s1 @ s2)
MM> / somechar* as s1 unclosed -> String.of_list s1
>>>
MM>
MM>Используется небольшой встроенный язык для описания парсеров, созданный при помощи синтаксического препроцессора. Этот вариант является субъективно куда более читабельным и объективно куда более расширяемым. Производительность можно увеличивать путём ускорения парсера (при этом ускоряются все программы, его использующие, а логика верхнего уровня не теряет в читабельности).
MM>Используется чёрная магия -- camlp4, путь к которому тернист и опасен. Пример приведён для ознакомления с возможностями OCaml и закрепления мотивации для освоения этого весьма неоднозначного, но неплохо подходящего для решения многих практических задач инструмента.
MM>
MM>Успехов!
Супер, покурю ваш вариантик.