24

私は現在 F# を学んでおり、FizzBu​​zz の (非常に) 簡単な例を試しました。

これは私の最初の試みです:

for x in 1..100 do 
    if x % 3 = 0 && x % 5 = 0 then printfn "FizzBuzz"  
    elif x % 3 = 0 then printfn "Fizz"
    elif x % 5 = 0 then printfn "Buzz"
    else printfn "%d" x

この問題を解決するために F# を使用すると、よりエレガント/シンプル/より優れた (理由を説明する) ソリューションは何ですか?

注: FizzBu​​zz の問題は、1 から 100 までの数字と、3 の倍数ごとに Fizz が出力され、5 の倍数ごとに Buzz が出力され、3 と 5 の両方の倍数ごとに FizzBu​​zz が出力されます。それ以外の場合は、単純な番号が表示されます。

ありがとう :)

4

11 に答える 11

58

あなたはすでに「最善の」解決策を持っていると思います。

より機能的な/F#イズムを誇示したい場合は、たとえば次のようにすることができます

[1..100] 
|> Seq.map (function
    | x when x%5=0 && x%3=0 -> "FizzBuzz"
    | x when x%3=0 -> "Fizz"
    | x when x%5=0 -> "Buzz"
    | x -> string x)
|> Seq.iter (printfn "%s")

リスト、シーケンス、マップ、反復、パターン、および部分適用を使用します。

[1..100]    // I am the list of numbers 1-100.  
            // F# has immutable singly-linked lists.
            // List literals use square brackets.

|>          // I am the pipeline operator.  
            // "x |> f" is just another way to write "f x".
            // It is a common idiom to "pipe" data through
            // a bunch of transformative functions.

   Seq.map  // "Seq" means "sequence", in F# such sequences
            // are just another name for IEnumerable<T>.
            // "map" is a function in the "Seq" module that
            // applies a function to every element of a 
            // sequence, returning a new sequence of results.

           (function    // The function keyword is one way to
                        // write a lambda, it means the same
                        // thing as "fun z -> match z with".
                        // "fun" starts a lambda.
                        // "match expr with" starts a pattern
                        // match, that then has |cases.

    | x when x%5=0 && x%3=0 
            // I'm a pattern.  The pattern is "x", which is 
            // just an identifier pattern that matches any
            // value and binds the name (x) to that value.
            // The "when" clause is a guard - the pattern
            // will only match if the guard predicate is true.

                            -> "FizzBuzz"
                // After each pattern is "-> expr" which is 
                // the thing evaluated if the pattern matches.
                // If this pattern matches, we return that 
                // string literal "FizzBuzz".

    | x when x%3=0 -> "Fizz"
            // Patterns are evaluated in order, just like
            // if...elif...elif...else, which is why we did 
            // the 'divisble-by-both' check first.

    | x when x%5=0 -> "Buzz"
    | x -> string x)
            // "string" is a function that converts its argument
            // to a string.  F# is statically-typed, so all the 
            // patterns have to evaluate to the same type, so the
            // return value of the map call can be e.g. an
            // IEnumerable<string> (aka seq<string>).

|>          // Another pipeline; pipe the prior sequence into...

   Seq.iter // iter applies a function to every element of a 
            // sequence, but the function should return "unit"
            // (like "void"), and iter itself returns unit.
            // Whereas sequences are lazy, "iter" will "force"
            // the sequence since it needs to apply the function
            // to each element only for its effects.

            (printfn "%s")
            // F# has type-safe printing; printfn "%s" expr
            // requires expr to have type string.  Usual kind of
            // %d for integers, etc.  Here we have partially 
            // applied printfn, it's a function still expecting 
            // the string, so this is a one-argument function 
            // that is appropriate to hand to iter.  Hurrah!
于 2010-03-11T05:20:43.890 に答える
26

私の例は、'ssp' によって投稿されたコードをわずかに改良したものです。パラメーター化されたアクティブ パターン (引数として除数を取る) を使用します。より詳細な説明は次のとおりです。

以下は、値が値で割り切れる かどうかをテストするために式で後で使用できるアクティブなパターンを定義します。書くとき:matchidivisor

match 9 with
| DivisibleBy 3 -> ...

...これは、値 '9' が として次の関数に渡されi、値3が として渡されることを意味しdivisorます。名前(|DivisibleBy|_|)は特別な構文であり、アクティブなパターンを宣言していることを意味します (そして、名前は matchの左側に表示され->ます。|_|ビットは、パターンが失敗する可能性があることを意味します (値が で割り切れない場合、この例は失敗しますdivisor) 。

let (|DivisibleBy|_|) divisor i = 

  // If the value is divisible, then we return 'Some()' which
  // represents that the active pattern succeeds - the '()' notation
  // means that we don't return any value from the pattern (if we
  // returned for example 'Some(i/divisor)' the use would be:
  //     match 6 with 
  //     | DivisibleBy 3 res -> .. (res would be asigned value 2)
  // None means that pattern failed and that the next clause should 
  // be tried (by the match expression)
  if i % divisor = 0 then Some () else None 

これで、すべての数値を反復処理し、それらをパターン (アクティブなパターン) と照合することができますmatch(またはSeq.iter、他の回答に示されている他の手法を使用):

for i in 1..100 do
  match i with
  // & allows us to run more than one pattern on the argument 'i'
  // so this calls 'DivisibleBy 3 i' and 'DivisibleBy 5 i' and it
  // succeeds (and runs the body) only if both of them return 'Some()'
  | DivisibleBy 3 & DivisibleBy 5 -> printfn "FizzBuzz"
  | DivisibleBy 3 -> printfn "Fizz" 
  | DivisibleBy 5 -> printfn "Buzz" 
  | _ -> printfn "%d" i

F# アクティブ パターンの詳細については、MSDN ドキュメント リンク を参照してください。コメントをすべて削除すると、元のバージョンよりもコードが少し読みやすくなると思います。それはいくつかの非常に便利なトリックを示しています:-)、しかしあなたの場合、タスクは比較的簡単です...

于 2010-03-12T01:15:26.553 に答える
12

F# スタイルのまだ 1 つのソリューション (つまり、アクティブ パターンの使用):

let (|P3|_|) i = if i % 3 = 0 then Some i else None
let (|P5|_|) i = if i % 5 = 0 then Some i else None

let f = function
  | P3 _ & P5 _ -> printfn "FizzBuzz"
  | P3 _        -> printfn "Fizz"
  | P5 _        -> printfn "Buzz"
  | x           -> printfn "%d" x

Seq.iter f {1..100}
//or
for i in 1..100 do f i
于 2010-03-11T14:23:14.827 に答える
11

考えられる答えをもう 1 つ追加するには、パターン マッチングを使用しない別のアプローチを示します。という事実を利用しているFizz + Buzz = FizzBuzzので、実際には 3 つのケースすべてをテストする必要はなく、3 で割り切れるかどうかを確認するだけで済み (次に "Fizz" を出力)、5 で割り切れるかどうかも確認できます (次に "Fizz" を出力します)。 "Buzz") 最後に、新しい行を出力します。

for i in 1..100 do
    for divisor, str in [ (3, "Fizz"); (5, "Buzz") ] do
        if i % divisor = 0 then printf "%s" str
    printfn ""

ネストされたループは、最初の反復でとにfor3 と "Fizz" を割り当て、次に 2 番目の反復で 2 番目の値のペアを割り当てます。利点は、値が7で割り切れる場合に「Jezz」の出力を簡単に追加できることです:-) ...ソリューションの拡張性が懸念される場合に備えて!divisorstr

于 2010-03-12T01:26:39.990 に答える
8

ここにもう1つあります:

let fizzy num =     
   match num%3, num%5 with      
      | 0,0 -> "fizzbuzz"
      | 0,_ -> "fizz"
      | _,0 -> "buzz"
      | _,_ -> num.ToString()

[1..100]
  |> List.map fizzy
  |> List.iter (fun (s:string) -> printfn "%s" s)
于 2014-04-13T14:49:28.850 に答える
6

これはもう少し読みやすい回答であることがわかりました。編集された回答は、他の人に少し触発されました

let FizzBuzz n =
    match n%3,n%5 with
    | 0,0 -> "FizzBuzz"
    | 0,_ -> "Fizz"
    | _,0 -> "Buzz"
    | _,_ -> string n

[1..100]
|> Seq.map (fun n -> FizzBuzz n)
|> Seq.iter (printfn "%s")
于 2014-05-11T21:13:30.033 に答える
1

これが私のバージョンです:

//initialize array a with values from 1 to 100
let a = Array.init 100 (fun x -> x + 1)

//iterate over array and match *indexes* x
Array.iter (fun x ->
    match x with
        | _ when x % 15 = 0 -> printfn "FizzBuzz"
        | _ when x % 5 = 0 -> printfn "Buzz"
        | _ when x % 3 = 0 -> printfn "Fizz"
        | _ -> printfn "%d" x
) a

これは F# での私の最初のプログラムです。

完璧ではありませんが、F# を学び始めた人 (私のように :)) は、ここで何が起こっているのかすぐに理解できると思います。

しかし、上記のパターン マッチングでのいずれ_かへのマッチングとそれ自体へのマッチングの違いは何だろうか。x

于 2010-09-15T19:37:17.030 に答える
1

i % 15 = 0のテストを含まない有効なソリューションが見つかりませんでした。それをテストしないことは、この「ばかげた」課題の一部であると常に感じていました。これは、言語での私の最初のプログラムであるため、おそらく慣用的な F# ではないことに注意してください。

for n in 1..100 do 
  let s = seq { 
    if n % 3 = 0 then yield "Fizz"
    if n % 5 = 0 then yield "Buzz" } 
  if Seq.isEmpty s then printf "%d"n
  printfn "%s"(s |> String.concat "")
于 2013-11-27T21:09:24.750 に答える