Как разобрать логическое выражение - PullRequest
0 голосов
/ 08 мая 2018

Я довольно новичок в F # и пытаюсь использовать рекурсию для решения проблемы.

Функция получает строку и возвращает bool. Строка анализируется и оценивается. Это логика логики, так что

  • (T | F) возвращает true
  • (T & (T & T)) возвращает true
  • ((T | T) & (T & F)) возвращает false
  • (F) = возвращает ложь

Моя идея состояла в том, чтобы каждый раз, когда я находил а), заменять часть строки из предыдущего (на это) результатом сравнения Сравнения. Делайте это снова и снова, пока не останется только T или F, чтобы вернуть true или false.

EDIT: Я ожидаю, что он возьмет строку и продолжит обменивать то, что находится между (и) с результатом сравнения, пока не дойдет до T или F. То, что происходит, является ошибкой в ​​отношении неполной структурированной конструкции. Ошибка в цикле for.

Поскольку я новичок в этом языке, я не уверен, что делаю не так. Вы это видите?

let ComparisonSolver (comp:string) =

    let mutable trim = comp
    trim <- trim.Replace("(", "")
    trim <- trim.Replace(")", "")

    match trim with
    | "T" -> "T"
    | "F" -> "F"
    | "!T" -> "F"
    | "!F" -> "T"
    | "T&T" -> "T"
    | "F&F" -> "T"
    | "T&F" -> "F"
    | "F&T" -> "F"
    | "T|T" -> "T"
    | "F|F" -> "F"
    | "T|F" -> "T"
    | "F|T" -> "T"
    | _ -> ""

let rec BoolParser arg =
    let mutable args = arg

    if String.length arg = 1 then 
        match arg with
        | "T" -> true
        | "F" -> false
    else
        let mutable ParseStart = 0
        let endRange = String.length args

        for letter in [0 .. endRange]
            if args.[letter] = "(" then
                ParseStart <- letter
            else if args.[letter] = ")" then
                args <- args.Replace(args.[ParseStart .. letter], ComparisonSolver args.[ParseStart .. letter])
                BoolParser args

let result = BoolParser "(T)&(F)"

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

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

http://www.quanttec.com/fparsec/

Еще один способ реализовать синтаксические анализаторы в F # - использовать активные шаблоны, которые могут создавать читабельный код

https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/active-patterns

Трудно предоставить хорошие отчеты об ошибках с помощью активных шаблонов, но, возможно, вы можете найти вдохновение в следующем примере:

let next s i = struct (s, i) |> Some

// Skips whitespace characters
let (|SkipWhitespace|_|) struct (s, i) =
  let rec loop j =
    if j < String.length s && s.[j] = ' ' then
      loop (j + 1)
    else
      next s j
  loop i

// Matches a specific character: ch
let (|Char|_|) ch struct (s, i) =
  if i < String.length s && s.[i] = ch then
    next s (i + 1)
  else
    None

// Matches a specific character: ch
//  and skips trailing whitespaces
let (|Token|_|) ch =
  function
  | Char ch (SkipWhitespace ps) -> Some ps
  | _                           -> None

// Parses the boolean expressions
let parse s =
  let rec term =
    function
    | Token 'T' ps                        -> Some (true, ps)
    | Token 'F' ps                        -> Some (false, ps)
    | Token '(' (Parse (v, Token ')' ps)) -> Some (v, ps)
    | _ -> None
  and opReducer p ch reducer = 
    let (|P|_|) ps = p ps
    let rec loop l =
      function
      | Token ch (P (r, ps))  -> loop (reducer l r) ps
      | Token ch _            -> None
      | ps                    -> Some (l, ps)
    function
    | P (l, ps) -> loop l ps
    | _         -> None 
  and andExpression ps  = opReducer term           '&' (&&) ps
  and orExpression ps   = opReducer andExpression  '|' (||) ps
  and parse ps          = orExpression ps
  and (|Parse|_|) ps    = parse ps
  match (struct (s, 0)) with
  | SkipWhitespace (Parse (v, _)) -> Some v
  | _                             -> None

module Tests = 
  // FsCheck allows us to get better confidence in that the parser actually works
  open FsCheck

  type Whitespace = 
    | Space

  type Ws = Ws of (Whitespace [])*(Whitespace [])

  type Expression =
    | Term  of Ws*bool
    | And   of Expression*Ws*Expression
    | Or    of Expression*Ws*Expression

    override x.ToString () =
      let orPrio              = 1
      let andPrio             = 2
      let sb                  = System.Text.StringBuilder 16
      let ch c                = sb.Append (c : char)  |> ignore
      let token (Ws (l, r)) c = 
        sb.Append (' ', l.Length) |> ignore
        sb.Append (c : char)      |> ignore
        sb.Append (' ', r.Length) |> ignore
      let enclose p1 p2 f     = 
        if p1 > p2 then ch '('; f (); ch ')'
        else f ()
      let rec loop prio =
        function
        | Term (ws, v)    -> token ws (if v then 'T' else 'F')
        | And  (l, ws, r) -> enclose prio andPrio <| fun () -> loop andPrio l; token ws '&' ;loop andPrio r
        | Or   (l, ws, r) -> enclose prio orPrio  <| fun () -> loop orPrio l ; token ws '|' ;loop orPrio r
      loop andPrio x
      sb.ToString ()

    member x.ToBool () =
      let rec loop =
        function
        | Term (_, v)     -> v
        | And  (l, _, r)  -> loop l && loop r
        | Or   (l, _, r)  -> loop l || loop r
      loop x

  type Properties() =
    static member ``Parsing expression shall succeed`` (expr : Expression) =
      let expected  = expr.ToBool ()  |> Some
      let str       = expr.ToString ()
      let actual    = str             |> parse
      expected      = actual

  let fscheck () =
    let config = { Config.Quick with MaxTest = 1000; MaxRejected = 1000 }
    Check.All<Properties> config
0 голосов
/ 09 мая 2018

Есть несколько вещей, которые нужно исправить.

  • for letter in [0 .. endRange] отсутствует do в конце - это должно быть for letter in [0 .. endRange] do
  • if сравнения в цикле for сравнивают chars с strings.Вам необходимо заменить "(" и ")" на '(', а ')'
  • for letter in [0 .. endRange] выйдет за пределы диапазона: в F # конструкция массива [x..y] перейдет с x на y включительно .Это немного похоже на C #, если у вас было for (int i = 0; i <= array.Length; i++).В F # вы также можете объявлять циклы следующим образом: for i = 0 to endRange - 1 do.
  • for letter in [0 .. endRange] снова выйдет за пределы диапазона: он переходит от 0 до endrange, что составляет длину args.Но args сокращается в цикле for, поэтому в конечном итоге он попытается получить символ из args, который находится вне диапазона.

Теперь проблема с if..then..elseоператоры, это то, на что, как мне кажется, вы смотрели с самого начала.

if args.[letter] = '(' then
 ParseStart <- letter
else if args.[letter] = ')' then
 args <- args.Replace(args.[ParseStart .. letter], ComparisonSolver args.[ParseStart .. letter])
 BoolParser args

Давайте возьмем код в двух ветвях как две отдельные функции.

Первая выполняет ParseStart <- letter,который присваивает letter ParseStart.Эта функция возвращает unit, что эквивалентно F # void.

Второе:

args <- args.Replace(args.[ParseStart .. letter], ComparisonSolver args.[ParseStart .. letter])
BoolParser args

Эта функция возвращает bool.

Сейчаскогда вы складываете их вместе в оператор if..then..else, который у вас есть в одной ветви, в результате получается unit, а в другой - bool.В этом случае он не знает, какой из них возвращать, поэтому он показывает ошибку «ожидалось, что выражение будет иметь тип».

Я сильно подозреваю, что вы хотели вызвать BoolParser args из снаружи цикл for / if.Но он имеет отступ, поэтому F # рассматривает его как часть оператора else if.

...