本文根据 Devhints / Goby 翻译。代码示例及版本范围沿用原文。原文标注更新日期:2018-12-06。
Goby 的语言设计基于 Ruby 语言,更加精简干练。两者在语法上的差异非常小。
入门
Hello world
hello.gb
class Greet
attr_accessor :audience, :head, :tail
def initialize
@head = "Hello, "
@tail = "!"
end
def name
audience.name
end
def say
puts head + name + tail
end
end
module MyName
attr_reader :name
def initialize
@name = self.class.to_s
end
end
class World
include MyName
end
greet = Greet.new
greet.audience = World.new
greet.say
然后运行:
$ goby hello.gb
#=> Hello, World!
REPL (igb)
$ goby -i
reset: 重置 VMexit: 退出 REPLhelp: 显示帮助- ctrl-c: 取消当前输入的代码块,或退出(在 top 顶层时)
参见 igb 手册与测试脚本. 您可以使用 readline 诸如通过方向键访问命令历史记录等功能。
变量
局部变量
zip101 = "233-7383"
magic_number = 42
应采用“[a-z][a-z0-9_]+”(蛇形命名法,snake_case)。
实例变量
module State
def initialize(state)
@state = state # declaring an instance variable by assignment
end
def show
@state # accessible from other instance methods
end
end
state = State.new "success"
state.show
#=> success
应采用“@[a-z][a-z0-9_]+”(蛇形命名法,snake_case)。
多重赋值
# array literal
a, b, c = [1, 2, 3]
# array with '*'
a = [1, 2, 3]
x, y, z = *a
# array literal with '*'
a, b, c = *[1, 2, 3]
# bare assignment: unsupported
a, b, c = 1, 2, 3 #=> unexpected 3 Line: 0
“黑洞”变量(丢弃变量)
# '_' is write-only
a, _ = [1, 2]
类变量
不支持。
全局变量
不支持。
方法定义
方法定义与调用
def foo_bar?(baz)
if baz == "Hi, Goby!"
true
else
false
end
end
foo_bar? "Hi, Goby!" #=> true
方法名称应采用“[a-z][a-z0-9_]+\??”(蛇形命名法,snake_case)。仅在没有参数时,可以省略末尾的“()”。!” 不被支持 不被支持.
方法参数的顺序
def foo(normal, default="value", hash={}, ary=[], keyword:, keyword_default:"key", *sprat)
end
如果为参数提供了默认值,则在调用时可以省略该参数。 () 可以省略。方法定义中参数的顺序限制如下:
- 普通参数 (例如
a) - 带有默认值的普通参数 (例如
a=1) - 可选参数 (数组或哈希,例如
ary=[]或hs={}) - 关键字参数 (例如
kwd:) - 带有默认值的关键字参数 (例如
kwd: 1或ary: [1,2,3]或hsh: {key: "value"}) - 不定长参数(splat 参数) (例如
*sp)
否则,将会报错。
关键字参数 (WIP)
def foo(process:, verb: :GET, opt:{ csp: :enabled }, ary: [1, 2, 3])
end
返回值
PI = 3.14
def area(radius)
radius * PI # returns the result of evaluation
end
area 6 #=> 18.84
返回多个值
def my_array
[1, 2, 3]
end
my_array #=> [1, 2, 3]
实例方法
module Foo
def bar # defining instance method
puts "bar"
end
def baz(count, email: "goby@example.com")
count.times do
puts email
end
end
end
foo = Foo.new
foo.bar #=> bar
foo.baz(3) #↓
goby@example.com
goby@example.com
goby@example.com
单例方法 #1
str = "Goby"
def str.foo #1 singleton method on the object
self * 2
end
str.foo
#=> GobyGoby
单例方法 #2
module Foo
def self.bar #2 singleton method with `self.`
92
end
end
单例方法 #3
module Foo
def Foo.bar #3 singleton method with a class name (unrecommended)
88
end
end
单例方法 #4
module Foo end
def Foo.bar #4 singleton methods outside the Foo
9999
end
Foo.bar #=> 9999
属性访问器方法
class Foo
attr_accessor :bar, :baz
def initialize
@bar = 42
@baz = 99
end
end
foo = Foo.new
foo.bar = 77
foo.baz = 88
您可以使用以下简写形式在类或模块中声明属性访问器方法:
attr_accessorattr_readerattr_writer
私有方法(待实现)
class Foo
def bar
42
end
def _baz # leading '_' means private method
99
end
end
模块/类定义
模块定义与 include
module Foo
def foo
"Foo's instance method"
end
end
class Bar
include Foo # to include Foo
end
Bar.new.foo #=> Foo's instance method
模块名称应采用“[A-Z][A-Za-z0-9_]+”(大驼峰命名法,UpperCamelCase)。模块无法被继承。
模块定义与 extend
module Foo
def foo
"Foo's instance method will be a singleton method"
end
end
class Bar
extend Foo # to extend Foo
end
Bar.foo #=> Foo's instance method will be a singleton method
extend 即在您的类或模块中,将指定模块内的实例方法用作 单例方法 。
模块实例化
module Foo #module definition
def foo
99
end
end
Foo.new.foo #=> 99
实际上,Goby 所指的模块甚至可以是 实例化 通过“new”——例如“Foo.new”.
类定义与继承
class Foo # class definition
def bar
99
end
end
class Baz < Foo # inheritance
end
Baz.new.bar #=> 99
类名应采用“[A-Z][A-Za-z0-9]+”(大驼峰命名法,UpperCamelCase)。支持通过“<”进行继承。
常量
HTTP_ERROR_404 = 404
HTTP_ERROR_404 = 500 # error
常量应采用“[A-Z][A-Za-z0-9_]+”(UPPER_SNAKECASE)。常量 不可重入 且其作用域为 全局.
重新定义类/模块
class Foo
def bar
99
end
end
class Foo
def bar # redefining is possible
77
end
end
命名空间
class Foo
module Bar
MAGIC = 99
def baz
99
end
end
end
Foo::Bar.new.baz # Use '::' for namespacing
Foo::Bar::MAGIC # Use '::' for namespacing
加载库
require
require("uri") # to activate URL class
u = URI.parse("http://example.com")
u.scheme #=> "http"
require_relative
require_relative("bar") # loading the local bar.gb
class Foo
def self.bar(x)
Bar.foo do |ten|
x * ten
end
end
def self.baz
yield(100)
end
end
字面量
关键字
def, true, false, nil, if, elsif, else, case, when, return, self, end, while, do, yield, get_block, next, class, module, break
字符串字面量
"double quote"
'single quote'
可以使用双引号或单引号。
符号字面量
:symbol # equivalent to "symbol"
{ symbol: "value" }
Goby 的符号(使用 :)始终为 String 类。
数值字面量
year = 2018 # Integer
offset = -42 # Integer
PI = 3.14 # Float
G = -9.8 # Float
数组字面量
[1, 2, 3, "hello", :goby, { key: "value"}]
[1, 2, [3, 4], 5, 6]
哈希(Hash)字面量
h = { key: "value", key2: "value2" }
h[:key2] #=> value2
哈希字面量的键应始终为 符号字面量.
范围字面量
(1..10).each do |x| # '..' represents a range
puts x*x
end
布尔值与 nil
true # Boolean class
false # Boolean class
nil # Null class
!nil #=> true
除……之外的任何对象 nil 以及 false 在条件判断中均被视为 true 。
运算符
算术/逻辑/赋值运算符
+ # unary
** # power
- # unary
* / % # multiplication, division, modulus
+ - # addition, subtraction
! # logical inversion
> >= < <= # inequality comparison
== != # equality comparison, negative comparison
&& # logical AND
|| # logical OR
+= -= # shorthand of addition/subtraction
= # assignment
*运算符优先级为 TBD
其他运算符
() # changing priority of interpretation
[] # array literal
* # multiple assignment
.. # range
*运算符优先级为 TBD
分隔符
class Foo; end # ';' to delimit
class Bar end # recommended
字符串插值(待实现)
puts "Error: #{error_message}" # double quotation is required
注释
puts "Goby" # comments
使用注解(annotations)来保持注释简洁。
TODOFIXMEOPTIMIZEHACKREVIEW
I/O
-
#puts -
特殊常量:
ARGV,STDIN,STDOUT,STDERR,ENV
支持 Flow 控制。
if, else, elsif
def foo(str)
if str.size > 10
puts "too big!"
elsif str.size < 3
puts "too short!"
else
puts "moderate"
end
end
then 是 非 支持的。
Break
def foo(tail)
(5..tail).each do |t|
if t % 2 == 0 && t % 5 == 0
puts "ouch!"
break # finish the block
else
puts t
end
end
puts "out of the block"
end
foo 20
#=> 5 6 7 8 9
#=> ouch!
#=> out of the block
大小写 (Case)
def foo(str)
case str
when "Elf"
puts "You might be Aragorn II!"
when "Aragorn"
puts "Long time no see, Aragorn!"
when "Frodo", "Sam", "Gandalf"
puts "One of us!"
else
puts "You're not yourself"
end
end
While
decr = 10
while decr do
if decr < 1
break
end
puts decr
decr -= 1
end
while、条件语句以及 do/end 代码块(block)均可用于构建循环。
Rescue
建设中。Join #605.
代码块
代码块
def foo(ary: [1, 2, 3])
ary.each do |s| # start of the block with |block variable|
puts s
end # end of the block
end
{ } 不能用于定义代码块。
yield
def foo
yield(10) # executes the block given
end
foo do |ten|
ten + 20
end
代码块对象(Block object)与 call
b = Block.new do
100
end
b.call #=> 100
Block.new 可以接收一个代码块,然后 call.
传递代码块
def baz
1000
end
class Foo
def exec_block(block)
block.call
end
def baz
100
end
end
b = Block.new do
baz
end
f = Foo.new
f.exec_block(b)
传递带有参数的代码块
b = Block.new do |arg, offset|
arg + 1000 - offset
end
b.call(49, 500) #=> 549
特殊 get_block 关键字
def bar(block)
# runs the block object and the block arg simultaneously
block.call + get_block.call
end
def foo
bar(get_block) do # passes two blocks to `bar`
20
end
end
foo do
10
end
get_block 它并非方法,而是 关键字 用于将指定的代码块参数获取为代码块对象。通过这种方式,你可以传递或 call 将指定的代码块参数作为代码块对象进行处理。
闭包(Closure)
count = 0 # the declaration is used
b = Block.new do
count += 1 # the block looks preserving the `count`
end
class Foo
def bar(blk)
count = 9 # (does not affect)
puts blk.call # local variable is resolved to the one above
end
end
Foo.new.bar b #=> 1
Foo.new.bar b #=> 2
Foo.new.bar b #=> 3
原生类(主要)
Goby 中最“原生”的类,原则上无法通过 new 进行实例化。
Object
Bar.ancestors
#» [Bar, Foo, Object]
Bar.singleton_class.ancestors
#» [#<Class:Bar>, #<Class:Object>, Class, Object]
Object 实际上仅用于创建单例类(singleton class)。参见 Class.
-
Object.methods:!,!=,==,block_given?,class,exit,instance_eval,instance_variable_get,instance_variable_set,is_a?,methods,nil?,object_id,puts,raise,require,require_relative,send,singleton_class,sleep,thread,to_s,<,<=,>,>=,ancestors,attr_accessor,attr_reader,attr_writer,extend,include,name,new,superclass -
Object.new.methods:!,!=,==,block_given?,class,exit,instance_eval,instance_variable_get,instance_variable_set,is_a?,methods,nil?,object_id,puts,raise,require,require_relative,send,singleton_class,sleep,thread,to_s
Class
String.ancestors #=> [String, Object]
Class 以及 Object实际上可以视为相同,在绝大多数情况下无需进行区分。
Class.methods:<,<=,>,>=,ancestors,attr_accessor,attr_reader,attr_writer,extend,include,name,new,superclass,!,!=,==,block_given?,class,exit,instance_eval,instance_variable_get,instance_variable_set,is_a?,methods,nil?,object_id,puts,raise,require,require_relative,send,singleton_class,sleep,thread,to_s
String
puts "Hello" + ' ' + 'world' #=> Hello world
固定为 UTF-8 ,并支持 mb4。
String.methods:fmt,- 其余部分:
Class.methods
- 其余部分:
"a".methods:!=,*,+,<,<=>,==,=~,>,[],[]=,capitalize,chop,concat,count,delete,downcase,each_byte,each_char,each_line,empty?,end_with?,eql?,fmt,include?,insert,length,ljust,match,new,replace,replace_once,reverse,rjust,size,slice,split,start_with,strip,to_a,to_bytes,to_d,to_f,to_i,to_s,upcase,- 其余部分:
Object.new.methods
- 其余部分:
Integer
37037 * 27 #=> 999999
Integer.methods:与Class.methods1.methods:!=,%,*,**,+,-,/,<,<=,<=>,==,>,>=,even?,new,next,odd?,pred,ptr,times,to_f,to_float32,to_float64,to_i,to_int,to_int16,to_int32,to_int64,to_int8,to_s,to_uint,to_uint16,to_uint32,to_uint64,to_uint8- 其余部分:
Object.new.methods
- 其余部分:
Array
[1, "2", :card, [4, 5], { john: "doe" }]
Array.methods:与Class.methods[1].methods:*,+,[],[]=,any?,at,clear,concat,count,delete_at,dig,each,each_index,empty?,first,flatten,include?,join,last,lazy,length,map,new,pop,push,reduce,reverse,reverse_each,rotate,select,shift,to_enum,unshift,values_at- 其余部分:
Object.new.methods
- 其余部分:
Hash
h = { key: "value" }
h = { "key": "value" } #=> error
h["key"] #=> value
h[:key] #=> value
哈希字面量(hash literal)中的键应为 符号字面量,而哈希索引既可以是字符串字面量,也可以是符号(symbol)字面量。
Hash.methods:与Class.methods{ key: "value" }.methods:[],[]=,any?,clear,default,default=,delete,delete_if,dig,each,each_key,each_value,empty?,eql?,fetch,fetch_values,has_key?,has_value?,keys,length,map_values,merge,new,select,sorted_keys,to_a,to_json,to_s,transform_values,values,values_at- 其余部分:
Object.new.methods
- 其余部分:
Range
(1..10).each do |i|
puts i ** 2
end
Range.methods:与Class.methods(1..10).methods:!=,==,bsearch,each,first,include?,last,lazy,map,new,size,step,to_a,to_enum- 其余部分:
Object.new.methods
- 其余部分:
Block
b = Block.new do
100
end
b.call #=> 100
Block.methods:与Class.methods(Block.new do end).methods:call- 其余部分:
Object.new.methods
- 其余部分:
原生类(次要)
Float
1.1 + 1.1 # => -2.2
2.1 * -2.1 # => -4.41
诸如……之类的浮点数字面量 3.14 或 -273.15. Float 类基于Golang 的 float64 类型。
Float.methods:与Class.methods3.14.methods:!=,%,*,**,+,-,/,<,<=,<=>,==,>,>=,new,ptr,to_d,to_i- 其余部分:
Object.new.methods
- 其余部分:
Decimal
"3.14".to_d # => 3.14
"-0.7238943".to_d # => -0.7238943
"355/113".to_d
# => 3.1415929203539823008849557522123893805309734513274336283185840
a = "16.1".to_d
b = "1.1".to_d
e = "17.2".to_d
a + b # => 0.1
a + b == e # => true
('16.1'.to_d + "1.1".to_d).to_s #=> 17.2
('16.1'.to_f + "1.1".to_f).to_s #=> 17.200000000000003
实验性功能:其大小是任意的,在内部实现上基于 Golang 的 big.Rat 类型。目前十进制字面量表示为 TBD,您可以通过 Decimal 数值,通过 to_d 方法从 Integer/Float/String.
Decimal.methods:与Class.methods(1.1).to_d.methods:!=,*,**,+,-,/,<,<=,<=>,==,>,>=,denominator,fraction,inverse- 其余部分:
Object.new.methods
- 其余部分:
Regexp
a = Regexp.new("orl")
a.match?("Hello World") #=> true
a.match?("Hello Regexp") #=> false
b = Regexp.new("😏")
b.match?("🤡 😏 😐") #=> true
b.match?("😝 😍 😊") #=> false
c = Regexp.new("居(ら(?=れ)|さ(?=せ)|る|ろ|れ(?=[ばる])|よ|(?=な[いかくけそ]|ま[しすせ]|そう|た|て))")
c.match?("居られればいいのに") #=> true
c.match?("居ずまいを正す") #=> false
使用方法 / / (待实现)。
Regexp.methods:与Class.methodsRegexp.new("^aa$").methods:==,match?- 其余部分:
Object.new.methods
- 其余部分:
MatchData
# numbered capture
'abcd'.match(Regexp.new('(b.)'))
#=> #<MatchData 0:"bc" 1:"bc">
# named capture
'abcd'.match(Regexp.new('a(?<first>b)(?<second>c)'))
#=> #<MatchData 0:"abc" first:"b" second:"c">
# converting to hash
» 'abcd'.match(Regexp.new('a(?<first>b)(?<second>c)')).to_h
#» { 0: "abc", first: "b", second: "c" }
捕获结果中的数字键实际上是 String class.The 键 0 对应的是匹配到的字符串。
MatchData.methods:与Class.methods'abcd'.match(Regexp.new('(b.)')).methods:captures,length,new,to_a,to_h- 其余部分:
Object.new.methods
- 其余部分:
File
f = File.new("../test_fixtures/file_test/size.gb")
f.name #=> "../test_fixtures/file_test/size.gb"
File.methods:basename,chmod,delete,exist?,extname,join- 其余部分:
Class.methods
- 其余部分:
File.new.methods:basename,chmod,close,delete,exist?,extname,join,name- 其余部分:
Object.new.methods
- 其余部分:
原生类(面向 Golang)
GoMap
h = { foo: "bar" }
m = GoMap.new(h) # to pass values to Golang's code
h2 = m.to_hash
h2[:foo] #=> "bar"
GoMap.methods:与Class.methodsGoMap.new.methods:get,set,to_hash- 其余部分:
Object.new.methods
- 其余部分:
Channel
c = Channel.new
1001.times do |i| # i start from 0 to 1000
thread do
c.deliver(i)
end
end
r = 0
1001.times do
r = r + c.receive
end
r #=> 500500
Channel 该类用于持有通道(channel),以便与 #thread配合使用。请参阅 thread.
Channel.methods:与Class.methodsChannel.new.methods:close,deliver,new,receive- 其余部分:
Object.new.methods
- 其余部分:
枚举器(Enumerator)与惰性求值(lazy)
一个较新的实验性库。
LazyEnumerator
# creating a lazy enumerator
enumerator = LazyEnumerator.new(ArrayEnumerator.new([1, 2, 3])) do |value|
2 * value
end
result = []
enumerator.each do |value|
result.push(value)
end
result #=> [2, 4, 6]
一种简写形式 #lazy 方法也已在 Array 以及 Range 中提供。请参阅下方的“技巧与提示”。
LazyEnumerator.methods:与Class.methods[1, 2].lazy:each,first,has_next?,initialize,map,next- 其余部分:
Object.new.methods
- 其余部分:
ArrayEnumerator
iterated_values = []
enumerator = ArrayEnumerator.new([1, 2, 4])
while enumerator.has_next? do
iterated_values.push(enumerator.next)
end
iterated_values #=> [1, 2, 4]
ArrayEnumerator.methods:与Class.methodsArrayEnumerator.new([1, 2, 3]).methods:has_next?,initialize,next- 其余部分:
Object.new.methods
- 其余部分:
RangeEnumerator
iterated_values = []
enumerator = RangeEnumerator.new((1..4))
while enumerator.has_next? do
iterated_values.push(enumerator.next)
end
iterated_values #=> [1, 2, 3, 4]
RangeEnumerator.methods:与Class.methodsRangeEnumerator.new(1..2).methods:has_next?,initialize,next- 其余部分:
Object.new.methods
- 其余部分:
特殊类
Boolean
true.class #=> Boolean
false.class #=> Boolean
一个专门用于持有 true 以及 false的特殊类。无法被实例化。
Null
nil.class #=> Null
一个专门用于持有 nil的特殊类。无法被实例化。
Method
(一个特殊的占位类,仅用于存放由 Goby 代码定义的方法。)
Diggable
提供 #dig 方法。目前。 Array 以及 Hash 类的实例可以被 Diggable.
[1, 2].dig(0, 1) #=> TypeError: Expect target to be Diggable, got Integer
测试框架
Spec
require "spec"
Spec.describe Spec do
it "fails and exit with code 1" do
expect(1).to eq(2)
end
end
Spec.run
Spec.methods:describe,describes,instance,run- 其余部分:
Object.methods
- 其余部分:
Spec.new.methods:describes,initialize,run,session_successful,session_successful=- 其余部分:
Hash.new.methods
- 其余部分:
技巧与提示
查看方法
» "string".methods
#» ["!=", "*", "+", "<", "<=>", "==", "=~", ">", "[]", "[]=", "capitalize", "chop", "concat", "count", "delete", "downcase", "each_byte", "each_char", "each_line", "empty?", "end_with?", "eql?", "fmt", "include?", "insert", "length", "ljust", "match", "new", "replace", "replace_once", "reverse", "rjust", "size", "slice", "split", "start_with", "strip", "to_a", "to_bytes", "to_d", "to_f", "to_i", "to_s", "upcase", "!", "block_given?", "class", "exit", "instance_eval", "instance_variable_get", "instance_variable_set", "is_a?", "methods", "nil?", "object_id", "puts", "raise", "require", "require_relative", "send", "singleton_class", "sleep", "thread"]
查看类
» "string".class
#» String
查看单例类
» "moji".singleton_class
#» #<Class:#<String:842352325152>>
» "moji".class.singleton_class
#» #<Class:String>
查看祖先类
» Integer.ancestors
#» [Integer, Object]
» "moji".class.ancestors
#» [String, Object]
查看单例类的祖先类
» "moji".class.singleton_class.ancestors
#» [#<Class:String>, #<Class:Object>, Class, Object]
查看对象的 ID
» "moji".object_id
#» 842352977920
#to_json
h = { a: 1, b: [1, "2", [4, 5, nil]]}
h.to_json # converts hash to JSON
#=> {"a":1, "b":[1, "2", [4, 5, null]]}
自定义 #to_json
重写 #to_json 在您的类中:
class JobTitle
def initialize(name)
@name = name
end
def to_json
{ title: @name }.to_json
end
end
class Person
def initialize(name, age)
@name = name
@age = age
@job = JobTitle.new("software engineer")
end
def to_json
{ name: @name, age: @age, job: @job }.to_json
end
end
stan = Person.new("Stan", 23)
h = { person: stan }
h.to_json #=> {"person":{"name":"Stan","job":{"title":"software engineer"},"age":23}}
惰性求值(Lazy)枚举
避免 N + 1 查询。
enumerator = [1, 2, 3].lazy.map do |value|
2 * value
end
result = []
enumerator.each do |value|
result.push(value)
end
result #=> [2, 4, 6]
您可以调用 #lazy.map 在 Array, Range上,或者 JSON 对象上。
代码风格
简明风格指南
- 应使用 UTF-8。
- 仅使用两个空格
作为一级缩进。- 不得使用制表符(Tab)进行缩进。
- 更多内容,请参考 RuboCop 风格指南 进行实例化。
文档标记法
Class#instance_method——使用#在文档中表示实例方法Class.class_methodModule.module_method
语法高亮
支持 Vim 和 Sublime Text。目前也可以使用 Ruby 的语法高亮配置。
参考资料
官方
- 官方网站: https://goby-lang.org/
- 代码仓库: https://github.com/goby-lang/goby/
- DevHints: https://devhints.io/goby (本页面)