此页面旨在为来自 Python 背景的用户提供学习 Raku 的方法。我们讨论了 Raku 中许多 Python 结构和习惯用法等效的语法。
基本语法§
你好,世界§
让我们从打印“你好,世界!”开始。Raku 中的 put 关键字等效于 Python 中的 print。与 Python 2 一样,括号是可选的。换行符将添加到行尾。
Python 2
print "Hello, world!"
Python 3
print("Hello, world!")
Raku
put "Hello, world!"
还有一个 say 关键字,其行为类似,但会调用其参数的 gist 方法。
Raku
my = "Hello, world!";say ; # also prints "Hello, world!"# same as: put $hello.gist
在 Python 中,'
和 "
是可以互换的。在 Raku 中,两者都可以用于引号,但双引号 ("
) 表示应执行插值。例如,以 $
开头的变量和包含在花括号中的表达式将被插值。
Raku
my = 'earth';say "Hello, $planet"; # OUTPUT: «Hello, earth»say 'Hello, $planet'; # OUTPUT: «Hello, $planet»say "Hello, planet number "; # OUTPUT: «Hello, planet number 3»
语句分隔符§
在 Python 中,换行符表示语句的结束。有一些例外:换行符之前的反斜杠会将语句跨行延续。此外,如果存在未匹配的左括号、左方括号或左花括号,则语句会跨行延续,直到匹配的花括号闭合。
在 Raku 中,分号表示语句的结束。如果分号是块的最后一个语句,则可以省略分号。如果紧跟在闭合花括号之后的是换行符,则也可以省略分号。
Python
print 1 + 2 + \
3 + 4
print ( 1 +
2 )
Raku
say 1 + 2 +3 + 4;say 1 +2;
块§
在 Python 中,缩进用于表示块。Raku 使用花括号。
Python
if 1 == 2:
print("Wait, what?")
else:
print("1 is not 2.")
Raku
if 1 == 2else
在条件语句中,两种语言都可选地使用括号,如上所示。
变量§
在 Python 中,变量在声明和初始化时同时进行。
foo = 12
bar = 19
在 Raku 中,my
声明符声明一个词法变量。变量可以使用 =
初始化。该变量可以先声明后初始化,也可以同时声明和初始化。
my ; # declare= 12; # initializemy = 19; # both at once
此外,您可能已经注意到,Raku 中的变量通常以符号开头——这些符号表示其容器的类型。以 $
开头的变量保存标量。以 @
开头的变量保存数组,以 %
开头的变量保存哈希(字典)。以 \
声明但未使用符号的无符号变量绑定到它们被分配的值,因此是不可变的。
请注意,从现在开始,我们将在大多数示例中使用无符号变量,以说明与 Python 的相似性。这在技术上是正确的,但通常情况下,我们将在需要或需要突出显示其不可变性(或在签名中使用时的类型独立性)的地方使用无符号变量。
Python
s = 10
l = [1, 2, 3]
d = { 'a' : 12, 'b' : 99 }
print(s)
print(l[2])
print(d['a'])
# 10, 3, 12
Raku
my = 10;my = 1, 2, 3;my = a => 12, b => 99;my \x = 99;say ;say [1];say <a>; # or %d{'a'}say x;# 10, 2, 12, 99
作用域§
在 Python 中,函数和类会创建一个新的作用域,但其他块构造器(例如循环、条件语句)不会创建作用域。在 Python 2 中,列表推导不会创建新的作用域,但在 Python 3 中,它们会创建新的作用域。
在 Raku 中,每个块都会创建一个词法作用域。
Python
if True:
x = 10
print(x)
# x is now 10
Raku
if Truesay# error, $x is not declared in this scope
my ;if Truesay# ok, $x is 10
Python
x = 10
for x in 1, 2, 3:
pass
print(x)
# x is 3
Raku
my \x = 10;for 1, 2, 3 -> \xsay x;# x is 10
Python 中的 Lambda 可以写成 Raku 中的块或尖括号块。
Python
l = lambda i: i + 12
Raku
my = ->
另一个用于构造 Lambda 的 Raku 习惯用法是 Whatever 星号,*
。
Raku
my = * + 12 # same as above
表达式中的 *
将成为参数的占位符,并在编译时将表达式转换为 Lambda。表达式中的每个 *
都是一个单独的位置参数。
有关子例程和块的更多构造,请参见下面的部分。
另一个示例(来自 Python FAQ)
Python
squares = []
for x in range(5):
squares.append(lambda: x ** 2)
print(squares[2]())
print(squares[4]())
# both 16 since there is only one x
Raku
my \squares = [];for ^5 -> \xsay squares[2]();say squares[4]();# 4, 16 since each loop iteration has a lexically scoped x,
请注意,^N
类似于 range(N)
。类似地,N..^M
类似于 range(N, M)
(从 N 到 M - 1 的列表)。范围 N..M
是从 N 到 M 的列表。..
前或后的 ^
表示应排除列表的开始或结束端点(或两者)。
此外,x²
是编写 x ** 2
(也正常工作)的一种简洁方式;Unicode 上标 2 对数字进行平方。许多其他 Unicode 运算符按预期工作(指数、分数、π),但每个可以在 Raku 中使用的 Unicode 运算符或符号都有一个 ASCII 等效项。
控制流§
Python 有 for
循环和 while
循环
for i in 1, 2:
print(i)
j = 1
while j < 3:
print(j)
j += 1
# 1, 2, 1, 2
Raku 也有 for
循环和 while
循环
for 1, 2 ->my = 1;while < 3
(Raku 还有一些其他循环构造:repeat...until
、repeat...while
、until
和 loop
。)
last
在 Raku 中退出循环,类似于 Python 中的 break
。Python 中的 continue
在 Raku 中是 next
。
Python
for i in range(10):
if i == 3:
continue
if i == 5:
break
print(i)
Raku
for ^10 ->
在 Raku 中,即使在列表推导之外,使用 `if` 作为语句修饰符(如上所示)也是可以接受的。
Python 中 `for` 循环内的 `yield` 语句,它会生成一个 `generator`,类似于 Raku 中的 `gather`/`take` 结构。它们都会打印 1、2、3。
Python
def count():
for i in 1, 2, 3:
yield i
for c in count():
print(c)
Raku
sub countfor count() ->
Python 中使用 `enumerate()` 和 `.items()` 机制来迭代列表或字典/映射,在 Raku 中都可以使用相同的 kv 方法来实现(因为列表的“键”是其类似数组的数字索引)。
Python
elems = ["neutronium", "hydrogen", "helium", "lithium"]
for i, e in enumerate(elems):
print("Elem no. %d is %s" % (i, e))
symbols = ["n", "H", "He", "Li"]
elem4Symbol = {s: e for s, e in zip(symbols, elems)}
for symbol, elem in elem4Symbol.items():
print("Symbol '%s' stands for %s" % (symbol, elem))
# Elem no. 0 is neutronium
# Elem no. 1 is hydrogen
# Elem no. 2 is helium
# Elem no. 3 is lithium
# Symbol 'H' stands for hydrogen
# Symbol 'He' stands for helium
# Symbol 'Li' stands for lithium
# Symbol 'n' stands for neutronium
Raku
my = <neutronium hydrogen helium lithium>;for .kv -> ,my = <n H He Li>;my ;= ;# Note that the iteration order will differ from Pythonfor .kv -> ,
Lambda 表达式、函数和子例程>§
在 Python 中使用 `def` 声明函数(子例程)在 Raku 中使用 `sub` 来完成。
def add(a, b):
return a + b
sub add(\a, \b) {
return a + b
}
`return` 是可选的;最后一个表达式的值将用作返回值。
sub add(\a, \b)
# using variables with sigilssub add(, )
Python 2 函数可以使用位置参数或关键字参数调用。这些由调用者决定。在 Python 3 中,某些参数可能是“仅关键字”。在 Raku 中,位置参数和命名参数由例程的签名决定。
Python
def speak(word, times):
for i in range(times):
print word
speak('hi', 2)
speak(word='hi', times=2)
Raku
位置参数
sub speak(, )speak('hi', 2);
命名参数以冒号开头
sub speak(:, :)speak(word => 'hi', times => 2);speak(:word<hi>, :times<2>); # Alternative, more idiomatic
Raku 支持多重分派,因此可以通过将例程声明为 `multi` 来提供多个签名。
multi speak(, )multi speak(:, :)speak('hi', 2);speak(:word<hi>, :times<2>);
命名参数可以使用多种格式发送
sub hello ;# all the samehello(name => 'world'); # fat arrow syntaxhello(:name('world')); # pair constructorhello :name<world>; # <> quotes words and makes a listmy = 'world';hello(:); # lexical var with the same name
可以使用 `sub`、块或尖括号块来创建匿名函数。
Python
square = lambda x: x ** 2
Raku
my = sub () ; # anonymous submy = -> ; # pointy blockmy = ; # placeholder variablemy = ; # topic variable
占位符变量按词法顺序排列以形成位置参数。因此这些是相同的
my = ;my = -> , ;
列表推导§
后缀语句修饰符和块可以组合起来,在 Raku 中轻松创建列表推导。
Python
print([ i * 2 for i in [3, 9]]) # OUTPUT: «[6, 18]»
Raku
say ( * 2 for 3, 9 ); # OUTPUT: «(6 18)»say ( for 3, 9 ); # OUTPUT: «(6 18)»say ( -> \i for 3, 9 ); # OUTPUT: «(6 18)»
可以应用条件语句,但 `if` 关键字位于首位,这与 Python 中 `if` 位于第二位的不同。
print [ x * 2 for x in [1, 2, 3] if x > 1 ] # OUTPUT: «[4, 6]»
vs
say ( * 2 if > 1 for 1, 2, 3 ); # OUTPUT: «(4 6)»
对于嵌套循环,交叉积运算符 `X` 将有所帮助
print([ i + j for i in [3,9] for j in [2,10] ]) # OUTPUT: «[5, 13, 11, 19]»
变为以下两者之一
say ( for (3,9) X (2,10) ); # OUTPUT: «(5 13 11 19)»say ( -> (\i, \j) for (3,9) X (2,10) ); # OUTPUT: «(5 13 11 19)»say ( -> (, ) for (3,9) X (2,10) );# OUTPUT: «(5 13 11 19)»say ( for (3,9) X (2,10) ); # OUTPUT: «(5 13 11 19)»
使用 `map`(与 Python 的 `map` 相同)和 `grep`(与 Python 的 `filter` 相同)是一种替代方法。
类和对象§
以下是一个来自 Python 文档 的示例。首先让我们了解一下在 Raku 中称为属性的“实例变量”。
Python
class Dog:
def __init__(self, name):
self.name = name
Raku
对于每个创建的类,Raku 默认提供构造函数方法 `new`,它接受命名参数。
Python
d = Dog('Fido')
e = Dog('Buddy')
print(d.name)
print(e.name)
Raku
my = Dog.new(:name<Fido>); # or: Dog.new(name => 'Fido')my = Dog.new(:name<Buddy>);say .name;say .name;
Raku 中的类属性可以通过几种方式声明。一种方法是声明一个词法变量和一个用于访问它的方法。
Python
class Dog:
kind = 'canine' # class attribute
def __init__(self, name):
self.name = name # instance attribute
d = Dog('Fido')
e = Dog('Buddy')
print(d.kind)
print(e.kind)
print(d.name)
print(e.name)
Raku
my = Dog.new(:name<Fido>);my = Dog.new(:name<Buddy>);say .kind;say .kind;say .name;say .name;
为了在 Raku 中修改属性,必须在属性上使用 `is rw` 特性。
Python
class Dog:
def __init__(self, name):
self.name = name
d = Dog()
d.name = 'rover'
Raku
my = Dog.new;.name = 'rover';
继承使用 `is` 完成。
Python
class Animal:
def jump(self):
print ("I am jumping")
class Dog(Animal):
pass
d = Dog()
d.jump()
Raku
is Animalmy = Dog.new;.jump;
可以使用 `is` 特性多次来实现多重继承。或者,它可以与 `also` 关键字结合使用。
Python
class Dog(Animal, Friend, Pet):
pass
Raku
; ; ;...;is Animal is Friend is Pet ;
或
; ; ;...;is Animal
装饰器§
Python 中的装饰器是将一个函数包装在另一个函数中的方法。在 Raku 中,这是使用 `wrap` 完成的。
Python
def greeter(f):
def new():
print('hello')
f()
return new
@greeter
def world():
print('world')
world();
Raku
sub world.wrap(sub ());world;
另一种方法是使用特性。
# declare the trait 'greeter'multi trait_mod:<is>(Routine , :)sub world is greeterworld;
上下文管理器§
Python 中的上下文管理器声明在进入或退出作用域时发生的事件。
以下是一个 Python 上下文管理器,它打印字符串“hello”、“world”和“bye”。
class hello:
def __exit__(self, type, value, traceback):
print('bye')
def __enter__(self):
print('hello')
with hello():
print('world')
对于“进入”和“退出”事件,将块作为参数传递是一种选择。
sub hello(Block )hello
一个相关的概念是 'Phasers',它可以设置为在进入或离开块时运行。
input
§
在 Python 3 中,`input` 关键字用于提示用户。此关键字可以提供一个可选参数,该参数将写入标准输出,但不带尾随换行符。
user_input = input("Say hi → ")
print(user_input)
提示后,您可以输入“Hi”或任何其他字符串,该字符串将存储在 `user_input` 变量中。这类似于 Raku 中的 prompt。
my = prompt("Say hi → ");say ; # OUTPUT: whatever you entered.
元组§
Python 元组是不可变序列。序列元素不需要是相同类型。
Python
tuple1 = (1, "two", 3, "hat")
tuple2 = (5, 6, "seven")
print(tuple1[1]) # OUTPUT: «two»
tuple3 = tuple1 + tuple2
print(tuple3) # OUTPUT: «(1, 'two', 3, 'hat', 5, 6, 'seven')»
Raku
Raku 没有内置的元组类型。您可以使用 Raku 的列表类型或外部模块来获得相同的效果。
my = (1, "two", 3, "hat");my = (5, 6, "seven");say [1]; # OUTPUT: «two»my = (slip(), slip());my = (|, |); # equivalent to previous linesay ; # OUTPUT: «(1, two, 3, hat, 5, 6, seven)»