Kotlin 返回块中的最后一个表达式

Kotlin 返回块中的最后一个表达式,kotlin,Kotlin,我正在学习新的非常漂亮的语言Kotlin,一切似乎都很符合逻辑和一致性。我只发现了一件事,它似乎是规则的一个任意例外,而不是一条可靠的规则。但也许我对规则背后的一些深层次原因理解不够 我知道在if else和语句中,当有代码块时,返回最后一个表达式。在下一个示例中,根据条件返回1或2——在本例中,它返回1 val x = if (1 < 2) {println("something"); 1} else {println("something else"); 2} 类似地,在函数体中,不

我正在学习新的非常漂亮的语言Kotlin,一切似乎都很符合逻辑和一致性。我只发现了一件事,它似乎是规则的一个任意例外,而不是一条可靠的规则。但也许我对规则背后的一些深层次原因理解不够

我知道在
if else
语句中,当有代码块时,返回最后一个表达式。在下一个示例中,根据条件返回
1
2
——在本例中,它返回
1

val x = if (1 < 2) {println("something"); 1} else {println("something else"); 2}
类似地,在函数体中,不会返回最后一个表达式。这甚至不编译

fun z() : Int {
    println("something")
    1
}

那么规则到底是什么呢?它是否真的如此任意:如果在用作表达式的
if else
when
语句中有一个代码块,则返回该块中的最后一个表达式。否则,最后一个表达式不会返回到外部范围。还是我遗漏了什么?

您误解了花括号
{}
,当使用所有
流控制
语句时,它只是一个块,例如:

if (condition) { //block here
} 
val lambda: () -> Int = { 1 }; // lambda
val lambda1: () -> Int = if (condition) { { 1 } } else { { 2 } };
val lambda2: () -> Int = if (condition) ({ 1 }) else ({ 2 });
val lambda3: () -> Int = if (condition) { -> 1 } else { -> 2 };
fun nothing(): Nothing {
    return ?;// a compile error raising
}
fun z() = 1;
单独声明
{}
时,它是lambda表达式,例如:

if (condition) { //block here
} 
val lambda: () -> Int = { 1 }; // lambda
val lambda1: () -> Int = if (condition) { { 1 } } else { { 2 } };
val lambda2: () -> Int = if (condition) ({ 1 }) else ({ 2 });
val lambda3: () -> Int = if (condition) { -> 1 } else { -> 2 };
fun nothing(): Nothing {
    return ?;// a compile error raising
}
fun z() = 1;
当您想在
if else
表达式中返回
lambda
时,必须将花括号
{}
加倍,或者使用括号
()
来区分块和lambda表达式,或者将
{}
显式设置为lambda,例如:

if (condition) { //block here
} 
val lambda: () -> Int = { 1 }; // lambda
val lambda1: () -> Int = if (condition) { { 1 } } else { { 2 } };
val lambda2: () -> Int = if (condition) ({ 1 }) else ({ 2 });
val lambda3: () -> Int = if (condition) { -> 1 } else { -> 2 };
fun nothing(): Nothing {
    return ?;// a compile error raising
}
fun z() = 1;
如果函数不返回任何有用的值,则其返回类型为
Unit
<代码>单位
是一种只有一个值的类型-
单位
。此值不必显式返回

另一方面,如果一个普通的
函数的返回类型不是
单元,那么它必须有一个显式的
返回
语句:

fun z(): Int { return 1; }
另一种情况是函数return
Nothing
,根本不允许使用
return
语句,因为您无法创建
Nothing
实例,例如:

if (condition) { //block here
} 
val lambda: () -> Int = { 1 }; // lambda
val lambda1: () -> Int = if (condition) { { 1 } } else { { 2 } };
val lambda2: () -> Int = if (condition) ({ 1 }) else ({ 2 });
val lambda3: () -> Int = if (condition) { -> 1 } else { -> 2 };
fun nothing(): Nothing {
    return ?;// a compile error raising
}
fun z() = 1;
当一个函数只有一个表达式时,您可以改为使用,例如:

if (condition) { //block here
} 
val lambda: () -> Int = { 1 }; // lambda
val lambda1: () -> Int = if (condition) { { 1 } } else { { 2 } };
val lambda2: () -> Int = if (condition) ({ 1 }) else ({ 2 });
val lambda3: () -> Int = if (condition) { -> 1 } else { -> 2 };
fun nothing(): Nothing {
    return ?;// a compile error raising
}
fun z() = 1;

lambda块与“普通”块之间存在差异,在您的情况下,“y”只是需要执行以获取返回值的lambda:

val block: () -> Int = { 5 }
val five: Int = { 5 }()
val anotherFive = block()
因此,如果您想要一个充当lambda的块,您可以创建一个lambda并立即用“()”执行它。这样,您的“z”函数将按如下方式编译:

funz():Int={
println(“某物”)
1.
}()


(当然,这没有多大意义,也不是很有效)

你完全正确,好吧。这个选择完全是武断的。嗯,可能不是完全任意的,但与选择大括号表示块和lambda所产生的解析复杂性有关

实际上,除了对块表达式进行处理外,还存在(简单)块的情况:只包含在大括号中的一组语句。例如,Java支持(简单)块,您可以编写:

String toBeComputed ; {
    // Relatively long sequence of operations here
    toBeComputed= resultingValue ;
}
我经常使用这个成语,因为:

  • 将代码提取到函数中并不总是很方便的
  • 与函数一样,它定义代码并记录其性质,而不引入其他名称或注释
  • 有时,最好同时初始化两个或多个变量,并引入一个具有此特定目的的结果值类,这样做感觉有些过分
  • 与函数一样,它也不会使用仅在内部使用的临时变量污染外部名称空间
有趣的是,由于Java不支持块表达式,我将把这个示例编写为

int y ; {
    println("something");
    y= 1 ;
}
其他语言(我想到ALGOL和Scala)确实支持块表达式(Scala也支持lambdas,但语法不同)。在这些语言中,您所建议的(Scala语法)

完全有效,不需要
()
强制lambda求值(因为没有lambda!)

总之,Kotlin的设计者确实选择了使语言在块和块表达式方面不如ALGOL和Scala一致,可能是为了方便


我希望我的长时间回应表明,你对Kotlin的期望不是不合逻辑的,只是因为一些语言设计的选择。这次少惊讶的原则不起作用。

是的,这可能就是我所说的<代码>{}
有三个(甚至更多?我是否缺少一些?)不同的角色-它可以是控制流语句中的块,也可以是lambda,也可以是函数体。不同的规则适用于所有这些情况。我不是要大声嚷嚷或抱怨,我必须接受事实。但在我看来,这似乎是一个不一致的语言设计缺陷,而不是完美的语言。我的编码风格使我从这个问题中摆脱出来,那就是当控制流语句用作表达式时,我会尽量避免它们中的块。我来自C++和Python领域,没有什么类似于“返回块中的最后表达式”,所以我将使用<代码> < <代码> >或< >代码>只有当返回的表达式是微不足道的(没有块)时才作为表达式,例如<代码> a=(x)y z z < />代码。否则我将使用完整的语法
if(x){println(“something”);a=y}else{println(“something”);a=z}
,就像我们从其他语言中知道的那样。该行不会将
y
赋值给任何东西。也许它赋予了MikeNakis一些东西,我的英语并不完美。我不确定哪一个是正确的:给变量赋值。或将变量赋值。但我认为很容易理解我的意思。:)