我对F#很陌生,我试图使用递归来解决问题。
该函数接收一个字符串,并返回一个bool。字符串被解析并进行评估。这是bool逻辑,所以
我的想法是每次我找到a)时,用比较匹配的结果替换前一个(到那个)的字符串部分。反复这样做,直到只有T或F为止,返回true或false。
编辑: 我希望它接受字符串,并继续交换(和)与比较结果之间的内容,直到它归结为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)"
答案 0 :(得分:4)
您需要纠正一些事项。
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
包含。如果你有for (int i = 0; i <= array.Length; i++)
,那就像在C#中一样。在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
语句的一部分。
答案 1 :(得分:1)
解析布尔表达式的方法有很多种。查看优秀的库FParsec可能是个好主意。
http://www.quanttec.com/fparsec/
在F#中实现解析器的另一种方法是使用可以生成可读代码的活动模式
https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/active-patterns
很难通过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