OCaml Проверка, если строка почти пуста или содержит ключевые слова

У меня есть проблема с OCaml, я сейчас изучаю его, но я все еще новичок. Я хотел бы сделать функцию, которая возвращает true, если строка пуста или содержит только пробелы, и в то же время удалить любое вхождение begin и end.

Я пробовал уже это:

let isEmptyString s =  
  let rec empty i =
    if i < 0 then true
    else
          let c = String.get s i in
          if c = ' ' || c = '\009' then empty (pred i)
          else false
  in
  s = Str.global_replace( Str.regexp "begin") "" s;
  s = Str.global_replace( Str.regexp "end") "" s;
  empty (pred (String.length s))

Но, очевидно, эта функция не работает так, как мне бы хотелось, потому что я все еще получаю begin в Formula.importNrAgentsFormula после ее вызова... Вот как я могу ее вызвать:

while true do
   let input = read_line () in
   if not (isEmptyString input) then
      let (nr, f) = Formula.importNrAgentsFormula input in
      incr counter;
      flush stdout;
      match choice with
       | "graph" -> printRes (Graph.isSat ~verbose:verb nr f)
       | _ -> printUsage ()
   else ()
done

Если бы кто-то с большим опытом работы с OCaml мог заметить и объяснить мне ошибку, я был бы рад :)

Заранее спасибо,

С уважением.


person Valentin Montmirail    schedule 06.06.2016    source источник
comment
Ответ Жюльена в порядке, но я бы кое-что добавил. Как вы это сделаете, вы прочитаете свою строку дважды, но вы можете сделать это только один раз, написав Str.global_replace( Str.regexp "\\(begin\\)\\|\\(end\\)") "" s   -  person Lhooq    schedule 06.06.2016


Ответы (2)


Я предлагаю вам позволить вашей функции isEmptyString (скорее isBlankString?) делать то, что она должна делать (просто проверьте, содержит ли она только пробелы или ничего), она не должна изменять исходную строку. Вы можете сделать это в своем цикле:

while true do
   let input = read_line () in
   let input = Str.global_replace( Str.regexp "begin") "" input in
   let input = Str.global_replace( Str.regexp "end") "" input in
   if not (isEmptyString input) then
   ...

Изменить. Приносим извинения за позднее редактирование. Вот дополнительная информация о вашей ошибке:

Если вы запустите свою функцию в OCaml, вы увидите это предупреждение:

Warning 10: this expression should have type unit.

на линии s = Str.global_replace( Str.regexp "begin") "" s;. Это связано с тем, что оператор = в OCaml в данном случае является не оператором присваивания, а оператором равенства, поэтому в этой строке вы просто сравниваете два значения и возвращаете логическое значение. Поскольку OCaml ожидает, что e1 в e1;e2 вернет unit, вы получите это предупреждение.

В OCaml значения переменных неизменяемы, поэтому вы можете:

  • Используйте другую переменную, как предлагает @Jason: let t = Str.global_replace( Str.regexp "begin") "" s
  • "затенить" старое значение, как я предлагаю выше: let s = Str.global_replace( Str.regexp "begin") "" s
  • Используйте ссылку (указатель на место в памяти): let s = ref "before" in s := "after", затем вы можете получить доступ к значению, указанному ссылкой, с помощью оператора !: !s. Однако, если вы изучаете функциональное программирование, я предлагаю вам постараться не использовать какие-либо императивные функции OCaml в начале, чтобы открыть для себя эту новую парадигму и ее возможности.
person Julien Lopez    schedule 06.06.2016

Поскольку я на работе, у меня нет с собой utop, но просто на первый взгляд, в вашем первом, в документации написано:

val global_replace : regexp -> string -> string -> string

Это означает, что вам не нужен ";" так как это когда функции возвращают единицу измерения и являются синтаксическим сахаром для чего-то вроде

let () = print_endline("foobar")

Кроме того, вам нужно использовать оператор let, поскольку вы не можете просто переназначить значение s. Я не рекомендую затенение переменной, так как обычно это плохая практика в функциональном программировании. Используйте что-то вроде:

    let t = (Str.global_replace( Str.regexp "begin") "" s)

Кроме того, ваша функция делает две разные вещи. Вспомогательная рекурсивная функция, которую вы написали, возвращает true и false, что хорошо (я предполагаю, что это работает). Однако то, для чего вы в конечном итоге используете его, — это то, что вы возвращаете. Следовательно, для первой функции вы на самом деле не возвращаете строку, если были заменены «начало» и «конец». Поэтому конечный результат вашей функции должен быть на самом деле кортежем типа (bool, string). Затем вы можете сопоставить его, когда вы его вызываете (например,

let b,s = isEmptyString "foobar" in
if not b then:
  rest of your code

Я считаю, что у вас есть правильное представление о вашей функции. Также во второй функции есть ли способ не использовать циклы и счетчики while? (Также, надеюсь, ваш счетчик реализован со ссылками, иначе у вас не будет ничего глобального). Я бы посоветовал повторить попытку с того места, где вы вызываете свою первую функцию, поскольку циклы и счетчики являются основой императивного программирования, а не функциональными (именно это делает OCaml таким

fun

:). Если нет, то ничего страшного, иногда есть вещи, которые вы не можете сделать в OCaml, не используя его императивные функции. Дайте мне знать, если эти предложения не сработают.

person Jason    schedule 06.06.2016