代码之家  ›  专栏  ›  技术社区  ›  Russell

如何用f编写fizzbuzz#

  •  22
  • Russell  · 技术社区  · 14 年前

    我目前正在学习f,并尝试了一个非常简单的fizzbuzz例子。

    这是我的初步尝试:

    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来解决这个问题?

    注意:fizz buzz的问题是通过数字1到100,每3个指纹的倍数fizz,每5个指纹的倍数buzz,每3个和5个指纹的倍数fizzbuzz。否则,简单地显示数字。

    谢谢)

    10 回复  |  直到 8 年前
        1
  •  52
  •   Brian    14 年前

    我认为你已经有了“最好”的解决方案。

    如果你想展示更多的功能/f-isms,你可以这样做。

    [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")
    

    使用列表、序列、映射、iter、模式和部分应用程序。

    [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!
    
        2
  •  24
  •   Tomas Petricek    14 年前

    我的示例只是对“ssp”发布的代码的一个小改进。它使用参数化的活动模式(以除数作为参数)。下面是一个更深入的解释:

    以下定义了 活动模式 我们以后可以在 match 要测试值是否 i 可被一个值整除 divisor . 当我们写作时:

    match 9 with
    | DivisibleBy 3 -> ...
    

    …这意味着值“9”将作为 以及价值 3 将作为 除数 . 名字 (|DivisibleBy|_|) 是一种特殊的语法,它意味着我们声明了一个活动模式(名称可以出现在 比赛 在左边 -> . 这个 |_| bit表示模式可能失败(当值不能被整除时,我们的示例失败 除数 )

    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 
    

    现在我们可以遍历所有的数字,并使用 比赛 (或使用 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活动模式的更多信息, here is an MSDN documentation link . 我认为如果删除所有注释,代码的可读性将比原始版本稍高。它显示了一些非常有用的技巧:-),但在您的情况下,任务相对简单…

        3
  •  12
  •   ssp    14 年前

    然而,有一个f风格的解决方案(即使用活动模式):

    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
    
        4
  •  10
  •   Tomas Petricek    14 年前

    再添加一个可能的答案-这里是另一个没有模式匹配的方法。它使用的事实是 Fizz + Buzz = FizzBuzz ,所以实际上不需要测试这三种情况,只需要查看它是否可以被3整除(然后打印“fizz”),也可以查看它是否可以被5整除(然后打印“buzz”),最后打印一行:

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

    嵌套的 for 循环分配3和“fizz”到 divisor str 在第一次迭代中,然后是在第二次迭代中的第二对值。好处是,当“jezz”的值可以被7:-)整除时,您可以很容易地添加“jezz”的打印……以防担心解决方案的可扩展性!

        5
  •  5
  •   Doug Ferguson    10 年前

    这里还有一个:

    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)
    
        6
  •  5
  •   user1477    10 年前

    我发现这是一个更有可读性的答案编辑的灵感有点来自其他人

    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")
    
        7
  •  1
  •   Grzegorz Gierlik    14 年前

    这是我的版本:

    //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 上面的图案匹配吗?

        8
  •  1
  •   Jonas Elfström    11 年前

    我找不到一个不包括 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 "")
    
        9
  •  0
  •   Snorex    11 年前

    下面是一个强调碳化作用的通用元组列表的版本:

    let carbonations = [(3, "Spizz") ; (5, "Fuzz"); (15, "SpizzFuzz");
                        (30, "DIZZZZZZZZ"); (18, "WHIIIIII")]
    
    let revCarbonated = carbonations |> List.sort |> List.rev
    
    let carbonRoute someCarbonations findMe =
        match(List.tryFind (fun (x,_) -> findMe % x = 0) someCarbonations) with
            | Some x -> printfn "%d - %s" findMe (snd x)
            | None -> printfn "%d" findMe
    
    let composeCarbonRoute = carbonRoute revCarbonated
    
    [1..100] |> List.iter composeCarbonRoute
    
        10
  •  0
  •   GHP    8 年前

    我不喜欢这些重复的字符串,这是我的:

    open System
    let ar = [| "Fizz"; "Buzz"; |]
    [1..100] |> List.map (fun i ->
        match i % 3 = 0, i % 5 = 0 with
            | true, false ->  ar.[0]
            | false, true ->  ar.[1] 
            | true, true ->  ar |> String.Concat
            | _ -> string i
        |> printf "%s\n"
    )
    |> ignore