在Swift中替代goto语句的方法

9

如何在Swift代码中将控制转移到特定行?

在Objective-C中,我会使用goto来执行以下操作:

if(a==b)
{
    goto i123;
}
else
{
    goto i456;
}
NSLog(@"the not reachable point");
i123:
NSLog(@"line 123 is here");
int j = 5;
int x = 2+j;
i456:
NSLog(@"line 456 is here");

我在Swift中找到的唯一控制转移语句是continuebreakfallthroughreturncontinuebreak只能与循环一起使用;returnfallthrough不能以这种方式传递控制。
那我应该使用什么? 编辑: Julien__的答案实际上没有解决我的问题,但它可能是现在唯一可用的选项。因此,我接受了Julien__的答案。

14
哦我的天啊。当然有一些情况下使用goto语句是很好的选择。但通常来说,你的代码看起来像50年代的FORTRAN语言。 - qwerty_so
2
这很容易导致未定义的行为,例如如果您在跳转到i456:之后使用x的值。Swift语言的一个目标是,编译器可以检查所有变量在使用之前是否已初始化。 - Martin R
3
我会说“使用函数”。goto最初是在没有子程序的程序中使用的一种解决方法。 - Sulthan
他们说在任何语言中使用goto是不好的编程实践,会使代码变得难以阅读。 - Vyachaslav Gerchicov
你可以通过运用行业技巧,如谨慎使用goto语句,更快地编写代码,为你的家庭和自己赚取更多的钱。(这是一位在加州从事全职编程已经50年的爷爷说的话) - Doug Null
8个回答

9
也许可以使用 switch 语句吗?
switch (a==b){
default:
    NSLog(@"the not reachable point");
    fallthrough­
case true:
    NSLog(@"line 123 is here");
    int j = 5;
    int x = 2+j;
    fallthrough­
case false:
    NSLog(@"line 456 is here");
}

编辑:以下是您可以进行后退操作的方法。

let START = 0
let STOP  = -1
var label = START

while(label != STOP){
    switch (label){

    default:
        label = START­

    case START:
        NSLog(@"the not reachable point");
        if a==b {
            label = 123
        } else {
            label = 456
        }

    case 123:
        NSLog(@"line 123 is here");
        int j = 5;
        int x = 2+j;
        fallthrough­

    case 456:
        NSLog(@"line 456 is here");
        fallthrough

    case STOP:
        label = STOP
    }
}

将您的代码包装在一个巨大(但组织良好)的switch语句中。您甚至可以创建一个名为goto的函数,以修改label变量的值。


2
为什么是-1?你能给个理由吗? - Julien__
5
@DemonSOCKET:我明白你的观点。但这里的问题不在于Swift没有类似GOTO的语句。问题在于你试图使用属于另一个时代的编程范式(GOTO)。我理解你有很多需要移植到Swift的代码,但你应该尝试用更现代化的方式重写代码。如果Swift真有你要找的GOTO等效语句,我会非常惊讶。个人看法。 - Luca Angeletti
1
这只允许前向跳转。您不能使用 switch 向后跳转。 - Nikolai Ruhe
历史上,您所描述的方法是用于第一个证明任何程序都可以在没有goto的情况下编写的方法。除了他们没有使用switch语句,而是使用长链if label == 1.. else if label == 2 .. else if label == 3... - gnasher729
1
Switch语句不能同时在多个线程上运行。使用GoTo可以手动控制代码中的位置,从一个线程跳到另一个线程(例如,在脚本中使用GCD进行延迟)。 - Albert Renshaw
显示剩余5条评论

8

嗯……实际上,Swift支持标签,可以用作控制流的goto,但不够灵活。以下代码来自于《Swift编程语言》:

gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 { diceRoll = 1 }

    switch square + diceRoll {
    case finalSquare:
    // diceRoll will move us to the final square, so the game is over
        break gameLoop
    case let newSquare where newSquare > finalSquare:
    // diceRoll will move us beyond the final square, so roll again
    continue gameLoop
    default:
    // this is a valid move, so find out its effect
        square += diceRoll
        square += board[square]
    }
}
print("Game over!")

完整的讨论可在《Swift编程语言(Swift 4.2):标记语句》中找到,于2018年9月13日访问。 - leanne
break loopLabel 和/或 continue myLoopLabel???这只是其他高级语言解决“如何从内部循环中断外部循环”的问题的复制。 - Top-Master
但是一个非常乐观的人可能会认为它是一个后退的goto ;-) - Top-Master

2
似乎Swift不希望任何人使用goto语句。这可能是为了避免未来很难跟踪的意大利面代码。
一个可能的替代方案是使用函数。函数有名称,具有意义,并且比单纯的行号更容易理解。

2

为了后人留存:

  • 文章《Swift中的goto》清晰地演示了如何实现goto风格的功能,包括不使用它们的警告和语言中缺少它们的理由。

  • 该文章作者还将其作为名为Goto.swift的Swift软件包在GitHub上提供。


1
这是什么意思?
var j : Int?
var x : Int?

if a == b {
    println("line 123 is here")
    j = 5
    x = 2 + j!
}
println("line 456 is here")

1
可能是因为这并没有回答问题(如何将控制转移到特定行?)。这只是针对这种特定情况的解决方法。 - Nikolai Ruhe

1

在支持GoTo的编程语言中,我认为GoTo并不总是一件坏事。我从不使用它在函数内部上下跳转,这会导致极大的混乱。但是我喜欢使用GoTo来给自己创建一个公共的退出点。以下是一个示例(伪代码)类似于这样:

func SomeFunction() -> Bool
{
    var iReturnValue = false;

    // Do some processing

    if(SomeCondition == true)
    {
        // return true;
        // No. Instead of having a return statement here.
        // I'd rather goto a common exit point.

        iReturnValue = true;
        goto ExitPoint;
    }

    // Do Some More processing

    if(SomeOtherCondition == SomeResult)
    {
        iReturnValue = false;
        goto ExitPoint;
    }

    //
    // More processing
    //

ExitPoint:
    // By having a common exit point I can do any
    // cleanup here and I've got a single return point
    // which I think makes my code easier to read.

    return iResultValue;
}

我知道我可以通过适当的使用大括号来实现相同的效果,但我发现使用经过良好测试的goto语句使生活更加简单。


看起来 guard 是你想要的。 - MANIAK_dobrii

0

我相信你的代码可以进行重构,避免使用goto。或者,你可以在函数内部使用函数,并仍然可以访问外部参数。例如,

func foobar(a: Int, b: Int) {

    func i123() {
        let j = 5
        let x = 2+j
        print("i123 x=\(x) b=\(b)")
    }

    func i456() {
        print("i456")
    }

    if a == b {
        i123()
    } else {
        i456()
    }
}

0
这是一个闭包({...}())的方法:
let done = false
while !done {
    {
        for formantA in editedFormants {
            for formantB in editedFormants {
                if abs(formantA - formantB) < MIN_DISTANCE {
                    let newFormant = (formantA + formantB) / 2
                    editedFormants.removeAtIndex(editedFormants.indexOf(formantA)!)
                    editedFormants.removeAtIndex(editedFormants.indexOf(formantB)!)
                    editedFormants.append(newFormant)
                    editedFormants = editedFormants.sort()
                    return
                }
            }
        }
        done = true
    }()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接