Re[2]: [OCaml] Попинайте код новичка
От: demi США  
Дата: 11.09.11 19:01
Оценка:
Здравствуйте, 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>
  1. Простой функциональный вариант:
    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>
  2. Декларативный вариант с 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>Успехов!


Супер, покурю ваш вариантик.
Не стыдно попасть в дерьмо, стыдно в нём остаться!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.