Algorithm 如何将非线性列表迭代方案抽象为可重用的算法?
有一个奇怪的机会,某人有一个绝妙的主意 我不确定是否有一个好的方法来概括这一点 编辑:我认为准确地解释输入和输出是什么可能会很好。下面的代码只是我处理解决方案的方式 输入:数据、配方 数据:字符串集,这里的字符串列表也称为“命名列表集”Algorithm 如何将非线性列表迭代方案抽象为可重用的算法?,algorithm,f#,dsl,Algorithm,F#,Dsl,有一个奇怪的机会,某人有一个绝妙的主意 我不确定是否有一个好的方法来概括这一点 编辑:我认为准确地解释输入和输出是什么可能会很好。下面的代码只是我处理解决方案的方式 输入:数据、配方 数据:字符串集,这里的字符串列表也称为“命名列表集” 配方:命令列表 命令打印(文字列表参考) 将文本添加到输出中,或者如果它是列表引用,则将引用列表的头添加到输出中 命令While(列表参考) 被引用时列表不为空-->下一个命令 被引用时,列表为空-->跳过配方列表中超过匹配Wend的条目 命令Wend(列表参考
配方:命令列表 命令打印(文字列表参考)
将文本添加到输出中,或者如果它是列表引用,则将引用列表的头添加到输出中 命令While(列表参考)
被引用时列表不为空-->下一个命令
被引用时,列表为空-->跳过配方列表中超过匹配Wend的条目 命令Wend(列表参考)
将引用列表替换为尾部(引用列表) 当引用列表为空时,下一个命令 当引用列表不为空时,下一个命令是上面的匹配命令 输出:字符串列表 最好的答案是实现最短的算法,并允许在新的上下文中重复使用该算法 顺便说一句,这不仅仅是一个有趣的编程问题。如果您尝试实现数据驱动的文本模板,基本上会发生这种情况 下面的代码是我试图解决这个问题的代码。 第一个代码段是一个非通用的解决方案。 第二个代码段是尝试隔离算法。 如果您使用代码,只需将第二个代码段粘贴到第一个代码段下面,两个版本都可以使用 整个主题是关于更好地理解如何将迭代算法与代码的其余部分分离,然后简单地应用它,而不是将所有其他代码都包含在其中 如果有一种方法可以抽象语句的处理方式和while/wend的循环,这不是很好吗, 它可以在我的主代码中重用,就像我不断重复使用其他“迭代方案”,比如List.map 我的主要代码与本研究的共同点是:
type MiniLanguage =
| Print of string
| While of string
| Wend of string
let testProgram =
[ Print("Hello, I am your Mini Language program")
While("names")
Print("<names>")
While("pets")
Print("<pets>")
Wend("pets")
Print("Done with pets.")
Wend("names")
Print("Done with names.")
]
type MiniEnvironment = { Bindings : Map<string,string>; Collections : Map<string, string list> }
let init collections =
{ Bindings = Map.empty; Collections = Map.ofList collections}
let bind n v env =
let newBindings =
env.Bindings
|> Map.remove n
|> Map.add n v
{ env with Bindings = newBindings; }
let unbind n env =
{ env with Bindings = Map.remove n env.Bindings; }
let bindingValue n env =
if env.Bindings.ContainsKey n then
Some(env.Bindings.Item n)
else
None
let consumeFirstFromCollection n env =
if env.Collections.ContainsKey n then
let coll = env.Collections.Item n
match coll with
| [] -> env |> unbind n
| _ ->
let first = coll.Head
let newCollections =
env.Collections
|> Map.remove n
|> Map.add n coll.Tail
{ env with Collections = newCollections }
|> bind n first
else failwith ("Unknown collection: " + n)
// All do functions take env - the execution environment - as last argument.
// All do functions return (a new) env as single return parameter.
let rec doPrint (s : string) env =
if s.StartsWith("<") && s.EndsWith(">") then
match bindingValue (s.Substring (1, s.Length - 2 )) env with
| Some(v) -> v
| _ -> s
else s
|> printfn "%s"
env
let rec skipPastWend name code =
match code with
| (Wend(cl) :: rest) when cl = name -> rest
| [] -> failwith "No Wend found!"
| (_ :: rest) -> skipPastWend name rest
let rec doWhileX name code env =
match code with
| (Print(s) :: rest) -> env |> (doPrint s) |> doWhileX name rest
| (While(cn) :: rest) -> env |> doWhile cn rest |> ignore; env |> doWhileX name (skipPastWend cn rest)
| (Wend(cn) :: rest) when cn = name -> env
| [] -> failwith ("While without Wend for: " + name)
| _ -> failwith ("nested while refering to same collection!")
and doWhile name code env =
let e0 = env |> consumeFirstFromCollection name
match bindingValue name e0 with
| Some(s) ->
e0 |> doWhileX name code |> doWhile name code
| None -> env
let rec run (program : MiniLanguage list) env =
match program with
| (Print(s) :: rest) -> env |> (doPrint s) |> run rest
| (While(cn) :: rest) ->
env
|> doWhile cn rest |> ignore
env |> run (skipPastWend cn program)
| (Wend(cn) :: rest) -> failwith "wend found in run()"
| [] -> env
let test() =
init [ "names", ["name1"; "name2"; "name3"; ]; "pets", ["pet1"; "pet2"] ]
|> run testProgram
|> printfn "%A"
(*
Running test() yields:
Hello, I am your Mini Language program
name1
pet1
pet2
Done with pets.
name2
pet1
pet2
Done with pets.
name3
pet1
pet2
Done with pets.
Done with names.
{Bindings = map [];
Collections =
map [("names", ["name1"; "name2"; "name3"]); ("pets", ["pet1"; "pet2"])];}
*)
类型小型语言=
|字符串打印
|弦乐
|线纹
让testProgram=
[打印(“你好,我是你的迷你语言程序”)
而(“姓名”)
打印(“”)
While(“宠物”)
打印(“”)
温德(“宠物”)
打印(“与宠物一起完成”)
温德(“姓名”)
打印(“使用名称完成”)
]
类型MiniEnvironment={Bindings:Map;Collections:Map}
让我们初始化集合=
{Bindings=Map.empty;Collections=Map.ofList Collections}
让我们绑定n v env=
让新发现=
环境绑定
|>删除
|>添加
{env with Bindings=newBindings;}
让我们解开纽扣=
{env with Bindings=Map.remove n env.Bindings;}
让bindingValue n env=
如果env.Bindings.ContainsKey n,则
一些(env.Bindings.Item n)
其他的
没有一个
让consumerfirstfromcollection n env=
如果env.Collections.ContainsKey n,则
设coll=env.Collections.Item n
搭配
|[]->env |>unbind n
| _ ->
let first=coll.Head
让新集合=
环境收藏
|>删除
|>映射添加n coll.Tail
{env with Collections=newCollections}
|>先绑定n
else failwith(“未知集合:+n”)
//所有do函数都将env(执行环境)作为最后一个参数。
//所有do函数都以单个返回参数的形式返回(一个新的)env。
让rec doPrint(s:string)env=
如果s.StartsWith(“”),则
将bindingValue(s.Substring(1,s.Length-2))env与
|一些(v)->v
|_uuS->s
其他
|>打印fn“%s”
环境
让记录跳过名称代码=
匹配代码
|当cl=name->rest时(Wend(cl)::rest
|[]->失败,并显示“未找到Wend!”
|(\:rest)->skipPastWend name rest
让rec doWhileX名称代码env=
匹配代码
|(打印):rest)->env |>(doPrint s)|>doWhileX name rest
|(While(cn)::rest->env |>doWhile cn rest |>ignore;环境|>doWhileX名称(skipPastWend cn rest)
|当cn=name->env时(Wend(cn)::rest
|[]->failwith(“而不使用Wend for:”+名称)
|->failwith(“引用同一集合时嵌套!”)
和doWhile名称代码env=
设e0=env |>consumeFirstFromCollection名称
将bindingValue名称e0与匹配
|部分-->
e0 |>doWhileX名称代码|>doWhile名称代码
|无->环境
让rec运行(程序:小型语言列表)env=
将程序与
|(打印):rest->env |>(doPrint s)|>运行rest
|(While(cn)::rest->
环境
|>doWhile cn rest |>忽略
环境|>run(skipPastWe
// The only function I had to "modify" to work with new "generalized" algorithm.
let consumeFirstFromCollection1 n env =
if env.Collections.ContainsKey n then
let coll = env.Collections.Item n
match coll with
| [] -> (env |> unbind n , false)
| _ ->
let first = coll.Head
let newCollections =
env.Collections
|> Map.remove n
|> Map.add n coll.Tail
({ env with Collections = newCollections }
|> bind n first , true)
else failwith ("Unknown collection: " + n)
type NamedList<'n,'t when 'n : comparison> = 'n * List<'t>
type Action<'a,'c> = 'c -> 'a -> 'a
type LoopPreparer<'a,'c> = 'c -> 'a -> 'a * bool
type CommandType = | RUN | BEGIN | END
type CommandClassifier<'c> = 'c -> CommandType
type Skipper<'c> = 'c -> List<'c> -> List<'c>
type InterpreterContext<'a,'c> =
{ classifier : CommandClassifier<'c>
executor : Action<'a,'c>
skipper : Skipper<'c>
prepareLoop : LoopPreparer<'a,'c>
isMatchingEnd : 'c -> 'c -> bool
}
let interpret (context : InterpreterContext<'a,'c>) (program : 'c list) (env : 'a) : 'a =
let rec loop front (code : 'c list) e =
let e0,hasData = e |> context.prepareLoop front
if hasData
then
e0
|> loop1 front (code)
|> loop front (code)
else e
and loop1 front code e =
match code with
| x :: more when (context.classifier x) = RUN ->
//printfn "RUN %A" x
e |> context.executor x |> loop1 front more
| x :: more when (context.classifier x) = BEGIN ->
//printfn "BEGIN %A" x
e |> loop x more |> ignore
e |> loop1 front (context.skipper x more)
| x :: more when (((context.classifier x) = END) && (context.isMatchingEnd front x)) -> /// && (context.isMatchingEnd front x)
//printfn "END %A" x
e
| [] -> failwith "No END."
| _ -> failwith "TODO: Not sure which case this is. But it is not a legal one!"
let rec interpr code e =
match code with
| [] -> e
| (first :: rest) ->
match context.classifier first with
| RUN -> env |> context.executor first |> interpr rest
| BEGIN ->
e |> loop first rest |> ignore
e |> interpr (context.skipper first rest)
| END -> failwith "END without BEGIN."
interpr program env
let test1() =
let context : InterpreterContext<MiniEnvironment,MiniLanguage> =
{ classifier = fun c-> match c with | MiniLanguage.Print(_) -> RUN | MiniLanguage.While(_) -> BEGIN | MiniLanguage.Wend(_) -> END;
executor = fun c env -> match c with | Print(s) -> doPrint s env | _ -> failwith "Not a known command.";
skipper = fun c cl -> match c with | While(n) -> skipPastWend n cl | _ -> failwith "first arg of skipper SHALL be While!"
prepareLoop = fun c env -> match c with | While(n) -> (consumeFirstFromCollection1 n env) | _ -> failwith "first arg of skipper SHALL be While!"
isMatchingEnd = fun cwhile cx -> match cwhile,cx with | (While(n),Wend(p)) when n = p -> true | _ -> false
}
init [ "names", ["name1"; "name2"; "name3"; ]; "pets", ["pet1"; "pet2"] ]
|> interpret context testProgram
|> printfn "%A"