Master-Detail场景需要F#优雅

bru*_*r10 1 f#

主 - 细节场景.永远都在.好吧,如果不是永远的话,至少我在70年代的打卡上用FORTRAN做了大师级细节.它仍然存在 - 关于STO的大量细节问题.

我找了一个很好的方法在F#中做一个主细节识别器,没找到它.抱歉,如果我错过了,如果是这样,有人可以回复sto线程或网址吗?谢谢

这是我的F#-newbie在F#中做一个主细节识别器.即:将原始细节字符串的原始/平面列表减少为F#-records列表,其中主字符串与其详细字符串列表配对.

不在这里寻找高尔夫代码.优雅.我曾希望最终得到优雅的东西,但下面只是一个直接的递归列表步行.我的F#-newbie大脑未能看到如何在这里好好利用折叠,理解,折叠,地图,活动模式,计算表达式等.

让它保持在F#中可以做到的事情.如果在.Net中有一个预先构建的平面文件主 - 详细XML数据加载器,它可以在一行.Net调用中将master-detail .txt文件转换为.Net XML,这非常有趣,因为它可以用于F#.

作为一个有着长期命令式编程历史的人,我试图坚持使用不可变的F#进行练习.但是如果在F#中使用命令式或可变代码真的是最好的方法,请解释一下.输出可以是元组列表,记录序列,元组数组等.

任何意见/反馈....谢谢

let testInput =
    ["master Homer"    ; "Doh.."; "Doh!!" ;
     "master Has none" ;
     "master JoyJoyJoy"; "Yaa!" ; "Yaa!!!"; "Yaa!!!!!!"]

type md = {m: string; d: string list}
            member x.addDetail newd = {m = x.m; d = x.d @ [newd]}
            static member  noMaster = {m = "" ; d =           []}  // master records can never be null-strings, so "" works here
            static member  isMaster (L:string) = L.StartsWith("master ")
            static member  isDetail (L:string) = not (md.isMaster L) // There is no third kind of record - if not a master then it is a detail

let rec masterDetails flatList currentMaster =
    if           md.noMaster = currentMaster then
        match flatList with
        | []     -> [] // If no master and no more input: input list was empty and the empty list is the overall result
        | h :: t -> if md.isMaster h then // If no master, then head becomes the first master of the run
                                          masterDetails t {m = h; d = []}
                    else
                        failwith "Bad input: First record must be a master record"
    else
        match flatList with
        | []     ->     [currentMaster]   // End of input; return current master as a one-entry-list
        | h :: t -> if md.isMaster h then // Head will now replace the current master as the new master
                        [currentMaster] @ masterDetails t {m = h; d = []}
                    else                  // Keep current master; and add detail record to current master's detail list
                                          masterDetails t (currentMaster.addDetail h)

let testSolution = // Required: 1) Preserve order of the master sets. 2) Preserve sort order of details-within-masters.
    [{m = "master Homer"    ; d = ["Doh.."; "Doh!!"             ]}; 
     {m = "master Has none" ; d = [                             ]};
     {m = "master JoyJoyJoy"; d = ["Yaa!"; "Yaa!!!"; "Yaa!!!!!!"]} ]

let            tryIt = masterDetails testInput md.noMaster
let testTry = (tryIt = testSolution)
Run Code Online (Sandbox Code Playgroud)

Bri*_*ian 5

这听起来像是一份工作takeDrop.

// split a list into a prefix of elements that all 
// meet predicate 'p', and the suffix remainder
let takeDrop p l =
    let rec loop acc l =
        match l with
        | h::t when p h -> loop (h::acc) t
        | _ -> List.rev acc, l
    loop [] l

let rec masterDetail input =
    [match input with
     | [] -> ()
     | h::t ->
        assert(md.isMaster h)
        let det, rest = takeDrop (not << md.isMaster) t
        yield { m = h; d = det }
        yield! masterDetail rest]
Run Code Online (Sandbox Code Playgroud)

完整的测试代码如下.

let testInput = 
    ["master Homer"    ; "Doh.."; "Doh!!" ; 
     "master Has none" ; 
     "master JoyJoyJoy"; "Yaa!" ; "Yaa!!!"; "Yaa!!!!!!"] 

type md = {m: string; d: string list} 
            static member  isMaster (s:string) = s.StartsWith("master ") 

let testSolution = // Required: 1) Preserve order of the master sets. 
                   // 2) Preserve sort order of details-within-masters. 
    [{m = "master Homer"    ; d = ["Doh.."; "Doh!!"             ]};  
     {m = "master Has none" ; d = [                             ]}; 
     {m = "master JoyJoyJoy"; d = ["Yaa!"; "Yaa!!!"; "Yaa!!!!!!"]} ] 

// split a list into a prefix of elements that all 
// meet predicate 'p', and the suffix remainder
let takeDrop p l =
    let rec loop acc l =
        match l with
        | h::t when p h -> loop (h::acc) t
        | _ -> List.rev acc, l
    loop [] l

let rec masterDetail input =
    [match input with
     | [] -> ()
     | h::t ->
        assert(md.isMaster h)
        let det, rest = takeDrop (not << md.isMaster) t
        yield { m = h; d = det }
        yield! masterDetail rest]

let briSol = masterDetail testInput
printfn "%A" (briSol = testSolution)
Run Code Online (Sandbox Code Playgroud)