2016-10-05 6 views
4

저는 F #에서 새롭기 때문에 C#/Java OOP의 오랜 세월 후에 내 사고 방식을 바꿀 수 없습니다.F # - 온전함 검사 및 옵션

나는 대화 상자를 열고 읽을 파일을 선택할 수있게 해주는 이벤트 처리기 MyForm.SelectFile(filePath:String)이 있습니다. 파일이 선택되면, Parser.LoadFile(filePath:String)가 호출된다 : "알파"와 "베타"

static member LoadFile(filePath:String) = 
    if not <| ZipFile.IsZipFile(filePath) then 
     failwith "invalid file specified." 
    use zipFile = new ZipFile(filePath) 
    if zipFile.Count <> 2 || zipFile |> Seq.exists(fun x -> x.FileName <> "alpha" && x.FileName <> "beta") then 
     failwith "invalid file specified." 
    zipFile |> fun x -> Parser.Parse(x.OpenReader()) 

난 항상 확장자없이이 개 파일이 포함 된 유효한 우편 아카이브로 선택한 파일을 기대하고있다.

먼저 입력을 살균하는 더 좋은 방법이 있습니까?

내 if 문이 꽤 길고 F #이 더 나은 솔루션을 제공 할 수 있다고 확신하지만 실제로 이해할 수는 없습니다.

둘째, failwith을 사용하면 내 MyForm.SelectFile(filePath:String) 메서드에서 예외를 처리해야하며 옵션을 사용하면 더 좋은 해결책이 될 수 있다고 생각합니다.

ZipFile을 인스턴스화해야하기 때문에 두 가지 연속 확인 (ZipFile.IsZipFile 및 내용)을 수행해야하는 경우 어떻게 사용하는지 알아낼 수 없습니다.

C#의 경우 확인이 실패 할 때마다 null을 반환하고 null에 대한 반환 값을 확인하면 오류를 묻지 않거나 계속해야하는지 알 수 있습니다.

현재 코드 :이 같은 기록 된 경우

type Parser with 

    static member isValidZipFile (zipFile:ZipFile) = 
     (zipFile.Count = 2) && (zipFile |> Seq.forall(fun x -> (x.FileName = "alpha") || (x.FileName = "beta"))) 

    static member LoadFile(filePath:String) = 
     if not <| ZipFile.IsZipFile(filePath) then 
      None 
     else 
      use zipFile = new ZipFile(filePath) 
      if not <| Parser.isValidZipFile(zipFile) then 
       None 
      else 
       Some(seq { for zipEntry in zipFile do yield Parser.Parse(zipEntry.OpenReader()) } |> Seq.toArray) 
+2

코드 검토에서 (컴파일 된) 코드의 더 완전한 부분을 제출하는 것을 고려하십시오. 코드를 기능적으로 구조화하는 방법에 대한 자세한 답변을 얻을 수 있습니다. – asibahi

답변

4

먼저, 함수의 마지막 줄이 좀 더 우아한 수 :

zipFile.OpenReader() |> Parser.Parse 

둘째, 올바른 방향으로 가고있는 것입니다 귀하의 생각은 최대한 멀리 Option입니다. 그것은이 경우에는 정말 아주 간단 :

zipFile.OpenReader() |> Parser.Parse |> Some 

지금, 당신은 당신이 긴 if 문을 좋아하지 않아 언급 :로 마지막 줄도 쓸 수

static member LoadFile(filePath:String) = 
    if not <| ZipFile.IsZipFile(filePath) then None else 
    use zipFile = new ZipFile(filePath) 
    if zipFile.Count <> 2 || zipFile |> Seq.exists(fun x -> x.FileName <> "alpha" && x.FileName <> "beta") then None else 
    Some (zipFile.OpenReader() |> Parser.Parse) 

있다. 함수로 바꾸자! 보통 "긍정적 인"이름을 가진 함수를 선호합니다. 즉 isValidInput 함수는 일반적으로 isInvalidInput보다 도움이됩니다.

let isValid (z:ZipFile) = 
    z.Count = 2 && z |> Seq.forAll(fun x -> x.FileName = "alpha" || x.FileName = "beta") 

는 이제 LoadFile 기능이 될 수 있습니다 :

static member LoadFile(filePath:String) = 
    if not <| ZipFile.IsZipFile(filePath) then None else 
    use zipFile = new ZipFile(filePath) 
    if not <| isValid zipFile then None else 
    zipFile.OpenReader() |> Parser.Parse |> Some 

을 그리고 그 읽기가 아주 쉽게 보이는, 그래서 우리는 지금 리팩토링을 중지 할 수 있도록 ZipFile를 실제로 유효한지의이 확인하는 기능을 만들어 보자 .

+0

도움 주셔서 대단히 감사합니다! 모든 것은 zip 내부의 모든 파일을 구문 분석하고 모든 구문 분석 결과를 반환해야한다는 사실을 제외하고는 매력처럼 작동합니다. 나는 실제 코드를 보여주는 내 대답을 업데이 트 ... 내가 뭘했는지는 올바른지 궁금하네요. Array가 파싱 결과를 저장하는 최선의 선택이라면 (필자는 일부는 zip 파일에 다시 작성해야합니다. 구문 분석 된 값을 변경하는 과정). –

2

이 코드 조각은 이상한입니다. 이와 같은 간단한 코드 조각에 대해 Sequence 식을 사용하면 잔인합니다.

Some(seq { for zipEntry in zipFile do yield Parser.Parse(zipEntry.OpenReader()) } |> Seq.toArray) 

당신이

zipFile |> Seq.map (fun ze -> ze.OpenReader() |> Parser.parse) |> Some 

처럼 더 잘 쓸 수 또는 당신이 (왜?) 배열에 그 일에 고집

zipFile |> Seq.map (fun ze -> ze.OpenReader() |> Parser.parse) |> Seq.toArray |> Some 

당신은 유형 서명하게 될 겁니다 option<seq<value>>. 이것이 좋은 생각인지 잘 모르겠지만 나머지 코드를 보지 않고도 말할 수는 없습니다.