crystal-lang入门(二)
CONTROL FLOW
Primitive Types
Nil
最简单的类型就是Nil,它只有一个值,意味数值不存在。
是否还记得String#index
。它返回一个nil值如果子字符串不存在于检索的字符串。它没有index,所以index位置不存在。
Bool
bool类型只有两个可能的值false和true。代表了布尔代数和逻辑真值。
布尔值对于管理程序中的控制流特别有用。
Boolean Algebra
下面这个例子展示了操作符根据布尔值执行布尔代数:
a = true b = false p! a && b, # conjunction (AND) a || b, # disjunction (OR) !a, # negation (NOT) a != b, # inequivalence (XOR) a == b # equivalence
Truthiness
布尔代数不限制类型仅为布尔类型。所有值有一个潜在真值:nil,false。以及null指针是falsey的,其他任一值(包括0)是truthy。
用"foo"和nil代替上例的true和false看看。
a = "foo" b = nil p! a && b, # conjunction (AND) a || b, # disjunction (OR) !a, # negation (NOT) a != b, # inequivalence (XOR) a == b # equivalence
AND和OR运算符返回与运算符真值匹配的第一个操作数。
p! "foo" && nil, "foo" && false, false || "foo", "bar" || "foo"
NOT
, XOR,
==总是返回一个布尔值
(true
或false
)。
Control Flow
Conditionals
message = "Hello World" if message.starts_with?("Hello") puts "Hello to you, too!" end
条件子句把代码分支放在门后,这个门只符合条件才开放。
最基本的形式包含了关键词if,其后跟着一个表达式作为条件语句。条件语句当表达式的返回值是truthy时才满足。所有后续的表达式都是分支的一部分直到由关键词end结束。
按照惯例,我们将嵌套的分支缩进两个空格。
上例打印message仅当它满足条件语句,Hello在字符串开头。
从技术上讲,这个程序仍然以预定义的顺序运行。fixed message总是匹配并使条件为真。但是假设我们没有在源代码中定义message的值。它也可以来自用户输入,例如聊天客户端。
如果message 有一个值但不是以Hello开始,那么程序会跳过条件分支并不打印任何东西。
条件表达式可以更复杂。使用布尔代数,我们可以构造一个接受Hello或Hi的条件:
message = "Hello World" if message.starts_with?("Hello") || message.starts_with?("Hi") puts "Hey there!" end
让我们把条件转换一下,只打印message当字符串不是以Hello为开头。
这只是与上一个示例的一个小偏差:我们可以使用否定运算符(!)把条件变成相反的表达式
message = "Hello World" if !message.starts_with?("Hello") puts "I didn't understand that." end
另一种方法是将if替换为关键字unless,它期望相反的真值。unless x相当于 if !x。
message = "Hello World" unless message.starts_with?("Hello") puts "I didn't understand that." end
让我们看一个使用String#index
查找子字符串并突出显示其位置的示例。还记得如果找不到子字符串,它返回nil吗?那样的话,我们什么都不能显示。所以我们需要一个if子句,条件是检查index是否为nil。.nil?方法是完美的。
str = "Crystal is awesome" index = str.index("aw") if !index.nil? puts str puts "#{" " * index}^^" end
编译器实际上强制您处理nil情况。尝试删除条件或将条件更改为true:将显示一个类型错误,并说明不能在该表达式中使用Nil值。在适当的条件下,编译器知道在分支内index不能为nil,它可以用作数字输入。
if !index.nil?的缩写是if index,两者基本上是等价的。只有当您想区分一个falsy值是nil还是false时,才有区别,因为前者的条件匹配false,而后者则不匹配。
Else
message = "Hello World" if message.starts_with?("Hello") puts "Hello to you, too!" end if !message.starts_with?("Hello") puts "I didn't understand that." end
让我们改进我们的程序,并在这两种情况下做出反应,不管消息是否满足条件。
我们可以将其作为两个独立的条件,条件为否定:
message = "Hello World" if message.starts_with?("Hello") puts "Hello to you, too!" else puts "I didn't understand that." end
这是可行的,但有两个缺点:条件表达式message.starts_with?(
“Hello”)计算两次,效率很低。后来,如果我们在一个地方更改了条件(可能也允许Hi),我们可能也会忘记更改另一个。
条件函数可以有多个分支。另一个分支由关键字else表示。如果不满足条件,则执行。
More branches
我们的程序只对Hello做出反应,但我们需要更多的交互。让我们添加一个分支来响应Bye。我们可以在同一个条件中有不同条件的分支。它就像一个else和另一个合成的if。因此关键字是elsif:
else分支仍然只在前面两个条件都不满足时执行。不过,它总是可以省略的。
请注意,不同的分支是互斥的,条件从上到下求值。在上面的例子中,这并不重要,因为这两个条件不能同时为真(message不能以Hello和Bye开头)。但是,我们可以添加一个非独占的替代条件来证明这一点:
message = "Bye World" if message.starts_with?("Hello") puts "Hello to you, too!" elsif message.starts_with?("Bye") puts "See you later!" else puts "I didn't understand that." end
这两个从句的分支条件相同,但顺序不同,它们的行为也不同。第一个匹配条件选择执行哪个分支。
message = "Hello Crystal" if message.starts_with?("Hello") puts "Hello to you, too!" elsif message.includes?("Crystal") puts "Shine bright like a crystal." end if message.includes?("Crystal") puts "Shine bright like a crystal." elsif message.starts_with?("Hello") puts "Hello to you, too!" end