2010-03-06 1 views
1

master-detail 시나리오. 영원히 계속되었습니다. OK, 영원히 아니더라도, 적어도 70 년대 펀치 카드에서 FORTRAN으로 마스터 디테일을하고있었습니다. 여전히 주위에 있습니다. STO에 대한 많은 세부적인 질문이 여기에 있습니다.F # Master-Detail 시나리오에 우아함 필요

F #에서 마스터 디테일 인식기를 수행하는 좋은 방법을 찾았지만 찾지 못했습니다. 죄송합니다, 만약 내가 그것을 놓친, 그리고 누군가가 단지 스레드 또는 URL로 회신 할 수 있습니까? 덕분에

다음은 F #에서 master-detail recognizer를 수행하는 나의 F # -newbie입니다. 즉 : 마스터 - 세부 문자열의 원시/플랫 목록을 마스터 문자열이 세부 정보 문자열 목록과 쌍을 이루는 F # - 레코드 목록으로 줄입니다.

여기 코드 골프를 찾고 있지 않습니다. 여러분. 우아. 나는 우아한 무엇인가로 끝나기를 바랐다. 그러나 아래는 단지 순회적인 재귀리스트 워크이다. 나의 F # -newbie 두뇌는 폴드, 이해, 폴드 - 백, 맵, 활성 패턴, 계산 표현식 등을 잘 사용하는 방법을 보지 못했습니다.

F #에서 할 수있는 일을 지키십시오. 닷넷 .Net XML로 마스터 디테일 .txt 파일을 만드는 .Net에 미리 빌드 된 플랫 파일 마스터 디테일 XML 데이터 로더가 있다면, 그것은 한 라인의 .Net 호출로 매우 유용합니다. 에프#.

긴 프로그래밍 명령 이력을 가진 사람으로서, 나는 연습용으로 불변의 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) 

답변

1

을 등 튜플의 목록, 기록의 순서, 튜플의 배열,

모든 의견/피드백이 될 수 없음이 없다 이 방식으로 목록을 자동으로 분할하는 내장 함수. 실제 세계에서는 처음에는 데이터의 다른 표현을 사용하기 때문에이 문제를 해결할 필요가 없습니다 (XML에서 데이터를로드 할 때 이미 계층 구조를 사용하고 데이터를 그룹화 할 때 LINQ, 계층 적 데이터도 얻을 수 있습니다.) 그러나 텍스트 파일에서 데이터를로드하는 경우와 같이 함수가 필요할 수도 있습니다.

다음은 시퀀스 표현식을 사용하여 (마스터 세부 정보 레코드의) 외부 컬렉션을 생성하는 약간 간단한 버전입니다. 내부 수집은 일반적인 방법으로 매개 변수에 축적된다 : [el]@rest를 사용하여 반대하고 반전으로이 훨씬 더 효율적이기 때문에 요소 (반전 된 순서에 축적되는 것을

let rec groupMasterDetails l acc master = seq { 
    match l with 
    // No master found yet, if the first element isn't master, we throw 
    | x::xs when not (md.isMaster x) && master = None -> 
    failwith "The first element must be master" 
    // Starting a new group, yield the previous group 
    | x::xs when md.isMaster x -> 
    if master <> None then yield { m = master.Value; d = List.rev acc } 
    yield! groupMasterDetails xs [] (Some x) 
    // Continue the current group 
    | x:: xs -> 
    yield! groupMasterDetails xs (x::acc) master 
    // End of processing, yield the last group 
    | [] -> 
    if master <> None then yield { m = master.Value; d = List.rev acc } } 

let masterDetails l = l [] None 

주 - @을 사용하여 전체 목록을 복사하는 것이므로 자주 사용하는 것은 나쁜 습관입니다). 이는 구현시 사용자의 addDetail 구성원이 필요하지 않음을 의미합니다.

그러나 이것은 비교적 긴 코드 조각입니다. 표준 F # 함수를 작성하여 구현할 수 있는지 알고 싶습니다. (이 방법을 찾지 못했습니다.)

+0

고마워요. 토마스. 네, 저는 제가 게시 한 두 개의 @을 좋아하지 않았습니다. 당신의 버전이 약간 단순한 것보다 낫다고 생각합니다. 두 개의 @를 찢고, 바깥 쪽을 찢어 버리고, 두 경기를 하나의 경기에 병합했습니다. 이해력/수율/수익률을 추가했습니다! 경비원이있을 때도 있습니다. 내 생각에는 약 6 개의 코드 경로 대신에 약 4 개의 코드 경로가 있습니다. 나는 네가 훨씬 더 좋아. 답장을 보내 주셔서 감사합니다. 네, 그게 제가 생각하고 있던 .txt 파일입니다. 오래된 사진 앨범 프로그램에서 데이터를 최신 Photoshop Elements로 옮길 수 있습니까? Voila, 폴더 당 마스터 - 세부 데이터의 하나의 txt 파일. – brucer10

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] 

아래의 전체 테스트 코드.

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) 
+0

+1, 우려의 좋은 분리. – gradbot

0

여기에는 Brain의 답을 바탕으로 분리가 너무 오래 걸리는 예가 있지만 기능 프로그래밍의 힘을 보여줍니다.

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 listSplit spliter neo l = 
    [match l with 
    | [] ->() 
    | h::t -> 
     let det, rest = spliter t 
     yield neo h det 
     yield! listSplit spliter neo rest] 

let masterDetail = 
    listSplit 
     (takeDrop (not << md.isMaster)) 
     (fun h det -> { m = h; d = det })