for (var i = 1; i <= 100; ++i) {
    var fizzBuzz = ""

    if i % 3 == 0 {
        fizzBuzz += "Fizz"
    }

    if i % 5 == 0 {
        fizzBuzz += "Buzz"
    }

    if fizzBuzz == "" {
        fizzBuzz += "\(i)"
    }

    println(fizzBuzz)
}


我不太喜欢将字符串与==进行比较,但是显然,这就是您在Swift中进行的操作(没有其他选择)。

if语句中的括号在Swift中是可选的。应该是这样,还是我们应该坚持下去?花括号在Objective-C(和许多编程语言)中是可选的,但在Swift中却不是。尽管它们以前是可选的,但我从未想到不使用它们是个好主意-在这里不使用括号是个好主意吗?

现在不明确声明变量的类型已经成为问题Swift(尽管变量仍然具有显式类型,但只是隐式确定)。可以隐式确定类型,还是应该坚持明确声明类型?

#1 楼


这是FizzBu​​zz Swift-y吗?


Kinda,但它可能要好得多。解决方法如下:




将此代码外推到方法中,然后从for循环中调用该方法。

func fizzbuzz(i: Int) -> String
{
   // ...
}



有一个方便的Swift功能,称为“ Tuples”。元组是值的分组。我们可以使用它们来表示取模运算的结果。

let result = (i % 3, i % 5)



现在我们正在使用元组来表示结果,我们可以轻松地使用switch执行必要的操作。

switch result
{
    case (0, 0):
        return "FizzBuzz"
    case (0, _):
        return "Fizz"
    case (_, 0):
        return "Buzz"
    default:
        return "\(i)"
}


您可能想知道下划线是什么。 _用来表示我们并不真正关心的舍弃值,而元组中的值在评估中对我们而言并不重要。


您的for循环不是很“迅速”。这就是我的写法,以便它调用该函数100次。

for number in 1...100
{
    println(fizzbuzz(number))
}





回答您的问题




if语句中的括号在Swift中是可选的。这应该是
,还是应该坚持下去?


这是一个非常注重样式的问题。风格因人而异,并且非常有机。由于尚未设置任何“样式规则”,因此请执行您最喜欢的任何事情。编程语言),但它们不在Swift中。


实现此功能是为了防止简化的if条件语句以及与它们相关的错误类。例如:

if (someCondition)
    doThisForSomeCondition()
    doThisAsWell()
if (someOtherCondition)
// ...


Swift强制使用花括号消除了在doThisAsWell()条件语句之外执行someCondition的情况。



可以隐式确定类型,还是应该
坚持显式声明类型?


是否包括显式类型取决于您的口味。在某些情况下,这可能会使您的代码更具可读性。但是,通常不是这种情况。由于编译器几乎永远不会将错误的类型分配给您的变量,因此我通常将类型保留为隐式确定。无论您采用哪种方法,都不会影响代码的速度/效率,因此这不是一个因素。



评论


\ $ \ begingroup \ $
为1 ... 100建议中的数字+1。我不认为元组和开关的实现是对原始实现的改进。元组有时很有用,但我不认为这是那个时候之一。我看到代码重复; “嘶嘶声”和“嗡嗡声”现在出现两次。另外,如果您尝试将其扩展到“ FizzBu​​zzPling”,则元组解决方案会变得笨拙,如此处建议的那样:codereview.stackexchange.com/questions/56708/…
\ $ \ endgroup \ $
–花岗岩罗伯特
2014年7月12日在2:24

\ $ \ begingroup \ $
@GraniteRobert这是由于该语言的设计所致:“与C和Objective-C中的switch语句相反,Swift中的switch语句不会落入每种情况的底部,默认情况下不会落入下一种情况。相反,整个switch语句将在第一个匹配的switch情况完成后立即完成其执行,而无需显式的break语句。”如果设计更像C,那么扩展它就不会麻烦得多。
\ $ \ endgroup \ $
–syb0rg
2014年7月12日在2:36



\ $ \ begingroup \ $
@ syb0rg:是的,我了解(并完全赞同)Swift案例不会像在C中那样失败。但是,无论采用哪种切换方式,我都认为这是当前问题的尴尬解决方案。也许我只需要更多的时间来习惯于在情况可能重叠且情况顺序很重要的地方切换语句。
\ $ \ endgroup \ $
–花岗岩罗伯特
2014年7月12日在2:47

\ $ \ begingroup \ $
您引用的指南与常见的Ruby最佳实践不匹配。 《 Ruby样式指南》更清晰:•有参数时,在括号中使用def。该方法不接受任何参数时,请省略括号。 •请勿在if / unless / while / until条件下使用括号。 •对于内部DSL一部分的方法,在Ruby中具有“关键字”状态的方法(例如attr_reader,puts)和属性访问方法,请在参数周围省略括号。在所有其他方法调用的参数周围使用括号。
\ $ \ endgroup \ $
– 200_success
2014年7月12日在2:55



\ $ \ begingroup \ $
我们不是在谈论Ruby,而是在谈论Swift。 :/
\ $ \ endgroup \ $
– nhgrif
2014年7月12日在12:06

#2 楼

在Swift的for循环中,括号是可选的,在Apple的所有书籍和示例代码中,通常都将其省略-您自己在if语句中这样做。此外,Swift有一个范围运算符(实际上是两个),因此无论如何您都应该使用它而不是手动增量。

这是在Swift中进行比较的唯一方法,许多人认为,无论如何,使用C / ObjC进行阅读更容易。

#3 楼

关于这些线的一些观察结果……

if fizzBuzz == "" {
    fizzBuzz += "\(i)"
}



这似乎是对字符串插值的不合理使用。 String(i)会更直接。
为什么麻烦连接到一个空字符串?

有了这两个更改……

if fizzBuzz == "" {
    fizzBuzz = String(i)
}


#4 楼

可选括号

我还没有看到有充分的理由在Swift表示可选的地方使用括号。在iffor中使用它们比在像foo = (x + y)这样的简单数学表达式中使用它们更有意义。或者某些人认为C咒语具有神秘的力量:return (foo);

如果您希望Swift看起来更像C,请继续添加可选的括号。但这对我来说似乎是一个不值得的目标,并且注定要失败。 :-)

==的比较

我有相反的看法;字符串是Swift中的普通对象,对我来说像==这样的普通比较运算符似乎很合理。在C之上构建Objective-C迫使我们采取了一些奇怪的做法。我愿意让他们离开。

#5 楼

这是我在操场上黑客入侵的一个简单的FizzBu​​zz:易于阅读。布尔标志应该比空字符串检查更有效,或者可能更有效。

假设您只是想转储到控制台。构建字符串并返回它也很容易进行修改。

#6 楼

我不知道这是Swift-y还是Ug-ly,或者也许两者都是。

我试图制作类似于Java中OldCurmudgeon的以数据为中心的FizzBu​​zzPling扩展的东西,但我没有无法与enum相提并论,但是找到了一种通过struct实现它的方法:

struct FizzBuzz
{
    var s: String
    var n: Int

    init(_ s: String, _ n: Int)
    {
        self.s = s
        self.n = n
    }

    func fizz(number: Int) -> String
    {
        return (number % self.n) == 0 ? self.s : ""
    }
}

let fizzBuzzArray = [FizzBuzz("Fizz", 3),
                     FizzBuzz("Buzz", 5),
                     FizzBuzz("Pling", 7)]

func fizzbuzz(number: Int) -> String
{
    var result = ""
    for fb in fizzBuzzArray
    {
        result += fb.fizz(number)
    }
    return (result == "") ? String(number) : result
}

for number in 1...106
{
    println(fizzbuzz(number))
}


FizzBu​​zzArray的初始化程序似乎太冗长,但是我不知道简化它。

我从200_success借用了String(number)建议。

#7 楼

我个人喜欢使用嵌入式运算符来保持Swift的紧密。这是我的解决方案:

for i in 1...100 {
    print( i % 3 != 0 && i % 5 != 0 ? i : (i % 3 == 0 ? "fizz" : "")+(i % 5 == 0 ? "buzz" : ""))
}


评论


\ $ \ begingroup \ $
我的上帝啊。是否违反了干净代码?
\ $ \ endgroup \ $
–纳兹穆尔·哈桑(Nazmul Hasan)
17 Mar 15 '17 15:40



#8 楼

我会将代码封装在一个函数中:

func fizzBuzz(i: Int) -> String {
    var str = "" //Allows concatenation, eliminating the need for redundant "Fizz" and "Buzz" Strings
    if i % 3 == 0 {
        str += "Fizz"
    }
    if i % 5 == 0 {
        str += "Buzz"
    }
    //If evenly divisible by both 3 and 5, str = FizzBuzz
    if str == "" {
        str = String(i)
    }
    //Else str = i
    return str
}


但是我不是使用循环,而是使用:

(1 ... 100).forEach { print(fizzBuzz(q4312078q)) }


在这里,我们创建一个包含数字1到100的Sequence协议实例(一个CountableRange)。通过遵循Sequence协议,我们可以访问forEach函数,该函数以闭包作为参数。

在Swift中,闭包是离散的代码块,可以从程序的其他地方调用(函数只是闭包的特殊情况)。当一个函数需要一个闭包,并且可以保证闭包的参数类型是什么时,该参数可以称为q4312079q。此外,当使用单行闭包时,可以省略函数的括号。

上面的代码为范围的每个元素运行指定的闭包(我们的函数),并打印结果。

#9 楼

集合,闭包和映射函数如何?

let output : [String] = Array<Int>(1...100).map { num in
    var str : String = ""
    if num % 3 == 0 {
        str += "Fizz"
    }
    if num % 5 == 0 {
        str += "Buzz"
    }
    return str.isEmpty ? String(num) : str
}

print(output.joined(separator:"\n"))


此解决方案从100个整数(1 ... 100)的数组开始,然后将该数组映射到闭包中具有转换函数的相同数目的字符串数组。闭包评估每个存储的整数,找出3和5的模,并为映射的元素构建字符串。

for循环的优点在于执行的灵活性。由于每个数组元素都映射到输出字符串数组中的一个数组元素,因此执行顺序无关紧要,并且可以通过执行map函数来优化和并行执行。由于这是一个赋值-整个程序是一个赋值(输出),因此此类执行可以推迟到使用点,例如惰性var或与Promise或Future异步。

输出存储在数组中,我们可以使用Collection函数在数组上做进一步的工作-过滤,排序和其他操作-在所示的示例中,所加入的函数可一次性生成整个输出字符串。

评论


\ $ \ begingroup \ $
您提出了一种替代解决方案,但尚未检查代码。请说明您的推理(您的解决方案如何工作以及为什么它比原始解决方案更好),以便作者和其他读者可以从您的思考过程中学习。
\ $ \ endgroup \ $
–丹农
18年3月15日在15:27