我正在尝试编写一个简单的解释器,使用F#控制海龟。
我创建了以下递归联合类型来表示海龟将接受的少数命令。代码将由“命令列表”表示,使用简单的递归函数执行不应该太难。
type Command =
| Repeat of amount * Command list
| Forward of amount
| Turn of direction * amount
我希望将这个解释器的空格划分清晰,使得源代码看起来如下。
Forward 75
Repeat 4
Forward 10
Turn Right 50
Repeat 6
Forward 20
Turn Right 60
Repeat 8
Forward 15
Turn Left 30
Turn Right 10
Forward 25
我有一个函数,根据缩进级别将所有内容分成int*字符串列表。所以Repeat 4变成(0, "Repeat 4"),Forward 10则变成(1, "Forward 10"),Turn Left 30则变成(3, "Turn Left 30")。
/// Creates indentation chart for generating a syntax tree by cycling
/// through a list of strings generated
/// by string.Split and counting the empty strings before each non-empty
//// string.
let indent strs =
let rec inner strs search cmds indent temp =
match strs, search with
| [], _ -> (indent,temp)::cmds
| ""::tail, Cmd -> inner tail Indent ((indent,temp)::cmds) 0 "" //
Newline started -> push cached counter and command string
| ""::tail, Indent -> inner tail Indent cmds (indent+1) temp // Next
level of indent -> increment indent counter
| h::tail, Cmd -> inner tail Cmd cmds indent (temp + " " + h)
| h::tail, Indent -> inner tail Cmd cmds indent (temp + h)
| h::tail, Start -> inner tail Cmd cmds indent (temp + h)
inner strs Start [] 0 "" |> List.rev
let splitIndent (text:string) = text.Trim().Split() |> Array.toList |>
indent
现在我遇到了困难。我有一系列带有缩进级别的命令列表,但我不知道如何动态创建递归式的联合类型。我知道如何硬编码它。它看起来像这样,
let tree = [
Forward 75
Repeat (4, [
Forward 10
Turn (Right, 50)
Repeat (6, [
Forward 20
Turn (Right, 60)
Repeat (8, [
Forward 15
Turn (Left, 30)
])])
Turn (Right, 10)])
Forward 25]
但我不知道如何根据不同的输入字符串生成类似这样的东西。
我阅读了许多与树、判别式联合和动态创建递归类型等相关的StackOverflow帖子,但没有找到符合我的需求的内容。我尝试修改我找到的少数示例,最接近的是Tomas Petricek在F# transform list to a tree中提供的答案。插入联合情况和模式匹配以使其正常工作使我更接近目标,但它省略了很多命令并重复了一些其他命令。
这是我迄今为止想出的最好方法,但它并不能得到所有的命令。
/// Takes the indent,command pairs list and produces a list of commands.
/// Some of these are nested in a tree structure based on their indent.
let buildProgram (diagram:(int*string) list) : Command list =
let rec collect indx lis commands =
match lis with
| [] -> commands
| x::xs ->
match fst x with
| i when i = indx ->
match split (snd x) with
| "Forward"::NUM n::tail -> collect i xs commands@[Forward
n]
| "Turn"::ARG a::NUM n::tail -> collect i xs
commands@[Turn(a,n)]
| "Repeat"::NUM n::tail -> commands@([(Repeat (n, (collect
(i+1) xs [])))] |> List.rev)
| i when i < indx ->
match split (snd x) with
| "Forward"::NUM n::tail -> collect (i-1) xs
commands@[Forward n]
| "Turn"::ARG a::NUM n::tail -> collect (i-1) xs
commands@[Turn(a,n)]
| "Repeat"::NUM n::tail -> commands@([(Repeat (n, (collect
(i-1) xs [])))] |> List.rev)
collect 0 diagram [] |> List.rev
如何根据不同的输入在运行时创建递归判别联合?