内容简介:欢迎关注“网易云课·光谷码农课堂”,V语言入门视频教程!
欢迎关注“网易云课·光谷 码农 课堂”,V语言入门视频教程!
-
中文文档:https://vlang-zh.cn/docs.html
-
中文译者:柴树杉https://github.com/chai2010
V语言是一个简单、快速、安全的编译型语言,比较适合于开发可维护的软件。
简介
V语言是一种静态类型的编译语言,用于构建可维护的软件。它与 Go 类似,同时也受到Oberon,Rust,Swift等语言设计的影响。
V语言也是一种非常简单的语言。通读本教程只要半个小时,你就可以掌握语言的全部特性。
尽管语言简单,但是为开发人员提供了强大的特性。任何其它语言可以实现的功能,V语言都可以实现。
QQ群和微信群
-
V语言QQ群:878358520
-
V语言微信群请关注“光谷码农”微信公众号,然后从底部菜单进群。
Hello World
fn main() {
println('hello V语言中文网:https://vlang-zh.cn')
}
函数用 fn 关键字定义或声明。返回类型在函数名称后面。在这个例子中main函数没有返回值,因此返回值类型被忽略了。
和 C语言 一样,main函数是程序的入口函数。println是内置函数之一,它打印到标准输出。
在一个单一文件的V程序中,main函数可以被忽略。这对于学习语言的一些小代码片段很友好。为了演示,后面的例子就忽略了main函数。
因此“Hello World”程序可以写的再简单一点:
println('hello V语言中文网:https://vlang-zh.cn')
注释
// 单行注释 /* 多行注释. /* 支持嵌套注释. https://vlang-zh.cn */ */
函数
fn main() {
println(add(77, 33))
println(sub(100, 50))
}
fn add(x int, y int) int {
return x + y
}
fn sub(x, y int) int {
return x - y
}
同样,类型在参数名称之后。
同样,和Go语言、C语言一样,函数不能重载。因为这样可以提高代码等可维护性和可读性。
函数可以在声明之前就使用:虽然
函数可以在声明之前使用:虽然add和sub在main之后声明,但是在main中就可使用。V语言中所有的声明都是可以提前使用,因此不用关心声明的顺序。
变量
name := 'Bob' age := 20 large_number := i64(9999999999) println(name) println(age) println(large_number)
变量使用 := 声明和初始化,这是V语言唯一定义变量的方式。因此,V语言中所有的变量必须指定一个初始化的值。
变量的类型是从右值推导来的。要使用其它类型,必须手工强制转换类型:使用 T(v) 表达式将v转换为T类型。
和其它主流语言不同的是,V语言只能在函数内部定义变量。V语言没有模块级别的全局变量,因此也没有全局状态。
mut age := 20 println(age) age = 21 println(age)
使用 = 给变量重新赋值。不过在V语言中,变量默认是不可再次改变的。如果需要再次改变变量的值,必须用 mut 修饰变量。可以尝试删除 mut ,然后再编译上面的代码。
需要注意 := 和 = 的差异,前者是用于声明和初始化变量,后者是重新给变量赋值。
fn main() {
age = 21
}
上面的代码将不能编译,因为变量没有被声明过。V语言中所有的变量必须要先声明。
fn main() {
age := 21
}
上面的代码依然不能被编译,因为V语言中禁止声明没有被使用的变量。
fn main() {
a := 10
if true {
a := 20
}
}
和很多其它语言不同的是,不同块作用域的变量不得重名。上面的例子中,变量a已经在外层被声明过,因此不能再声明a名字的变量。
基础类型
bool string i8 i16 i32 i64 i128 (soon) u8 u16 u32 u64 u128 (soon) byte // alias for u8 int // alias for i32 rune // alias for i32, represents a Unicode code point f32 f64 byteptr voidptr
和C语言、Go语言不同的是,int始终是32bit大小。
字符串
name := 'Bob'
println('Hello, $name!') // `$` is used for string interpolation
println(name.len)
bobby := name + 'by' // + is used to concatenate strings
println(bobby) // ==> "Bobby"
println(bobby.substr(1, 3)) // ==> "ob"
// println(bobby[1:3]) // This syntax will most likely replace the substr() method
V语言中,字符串是一个只读的字节数组。字符串数据采用UTF8编码。
单引号和双引号都可以用户包含字符串面值(TODO:双引号目前还不支持)。为保持一致性,vfmt会将双引号字符串转换为单引号,除非该字符串包含单引号字符。
因为字符串是只读的,因此字符串的取子字符串的操作会比较高效:不需要复制,也不需要额外分配内存。
V语言中运算符两边值的类型必须是一样的。比如下面的代码,如果age是int类型的话,是不能正确编译的:
println('age = ' + age)
我们需要将age转换为string类型:
println('age = ' + age.str())
或者在字符串内部直接嵌入表达式(这是比较完美的方式):
println('age = $age')
数组
nums := [1, 2, 3]
println(nums)
println(nums[1]) // ==> "2"
mut names := ['John']
names << 'Peter'
names << 'Sam'
// names << 10 <-- This will not compile. `names` is an array of strings.
println(names.len) // ==> "3"
println('Alex' in names) // ==> "false"
// We can also preallocate a certain amount of elements.
nr_ids := 50
mut ids := [0 ; nr_ids] // This creates an array with 50 zeroes
数组的第一个元素决定来数组的类型,比如 [1, 2, 3] 对应整数类型的数组 []int 。而 ['a', 'b'] 对应字符串数组 []string 。
数组中的每个元素必须有相同的类型,比如 [1, 'a'] 将不能编译。
<< 运算符用于向数组的末尾添加元素。
而数组的 .len 成员返回数组元素的个数。这是一个只读的属性,用户不能修改。V语言中所有导出的成员默认都是只读的。
val in array 表达式判断val值是否是在数组中。
Maps
mut m := map[string]int{} // Only maps with string keys are allowed for now
m['one'] = 1
println(m['one']) // ==> "1"
println(m['bad_key']) // ==> "0"
// TODO: implement a way to check if the key exists
numbers := { // TODO: this syntax is not implemented yet
'one': 1,
'two': 2,
}
If
a := 10
b := 20
if a < b {
println('$a < $b')
} else if a > b {
println('$a > $b')
} else {
println('$a == $b')
}
if语句和大多数编程语言类似。和C语言不同的是,条件部分不需要小括弧,而大括弧是必须的。
if同时也可以当作表达式使用:
num := 777
s := if num % 2 == 0 {
'even'
}
else {
'odd'
}
println(s) // ==> "odd"
`in`运算符
in 运算符判断数组是否包含某个元素。
nums := [1, 2, 3] println(1 in nums) // ==> true
对于需多个值之一的相等判断比较简洁:
if parser.token == .plus || parser.token == .minus || parser.token == .div || parser.token == .mult {
...
}
if parser.token in [.plus, .minus, .div, .mult] {
...
}
V语言会优化上述的表达式,因此两种方式产生的目标代码都是差不多的。
for循环
V语言只有for一种循环结构。
numbers := [1, 2, 3, 4, 5]
for num in numbers {
println(num)
}
names := ['Sam', 'Peter']
for i, name in names {
println('$i) $name') // Output: 0) Sam
}
其中 for .. in 循环用于迭代遍历数组中每个元素的值。如果同时还需要元素对应的索引的话,可以用 for index, value in 语法。
mut sum := 0
mut i := 0
for i <= 100 {
sum += i
i++
}
println(sum) // ==> "5050"
这种风格的循环和其它语言中的while循环类似。当循环条件为false的时候结束循环迭代。
同样,循环条件不需要小括弧,而大括弧又是必须的。
mut num := 0
for {
num++
if num >= 10 {
break
}
}
println(num) // ==> "10"
循环的条件可以省略,省略后类似一个无限循环。
for i := 0; i < 10; i++ {
println(i)
}
最后是C语言风格的for循环。这种方式的循环比while循环更安全,因为while循环很容易忘记更新循环的计数器。
这里的 i 不需要用 mut 声明,因为这里的变量默认是可变的。
`switch`多分支
os := 'windows'
print('V is running on ')
switch os {
case 'darwin':
println('macOS.')
case 'linux':
println('Linux.')
default:
println(os)
}
// TODO: replace with match expressions
switch 对应多个 if - else 分支的简化。当遇到相等的第一个case对应的语句执行相应的语句。和C语言不同的是,不需要在每个case写break。
结构体
struct Point {
x int
y int
}
p := Point{
x: 10
y: 20
}
println(p.x) // Struct fields are accessed using a dot
上面的结构体都在栈上分配。如果需要在堆上分布,需要用取地址的 & 操作符:
pointer := &Point{10, 10} // Alternative initialization syntax for structs with 3 fields or fewer
println(pointer.x) // Pointers have the same syntax for accessing fields
V语言不支持子类继承,但是可以嵌入匿名结构体成员:
// TODO: this will be implemented later in June
struct Button {
Widget
title string
}
button := new_button('Click me')
button.set_pos(x, y)
// Without embedding we'd have to do
button.widget.set_pos(x,y)
结构体成员访问修饰符
结构体成员默认是私有并且不可修改的(结构体模式是只读)。但是可以通过 pub 设置为公开的,通过 mut 设置为可写的。总体来说有以下五种组合类型:
struct Foo {
a int // private immutable (default)
mut:
b int // private mutable
c int // (you can list multiple fields with the same access modifier)
pub:
d int // public immmutable (readonly)
pub mut:
e int // public, but mutable only in parent module
pub mut mut:
f int // public and mutable both inside and outside parent module
} // (not recommended to use, that's why it's so verbose)
例如在builtin模块定义的字符串类型:
struct string {
str byteptr
pub:
len int
}
可以看出字符串是一个只读类型。
字符串结构体中的byte指针在builtin模块之外不可访问。而len成员是模块外部可见的,但是外部是只读的。
fn main() {
str := 'hello'
len := str.len // OK
str.len++ // Compilation error
}
方法
struct User {
age int
}
fn (u User) can_register() bool {
return u.age > 16
}
user := User{age: 10}
println(user.can_register()) // ==> "false"
user2 := User{age: 20}
println(user2.can_register()) // ==> "true"
V语言没有类,但是可以基于类型定义方法。
方法是一种带有接收者参数的特殊函数。
接收者参数出现在fn关键字和方法名字之间,方法名之后也可以有普通的参数。
在上面的例子中,can_register方法有一个User类型的接收者参数u。V语言的习惯是不要用self或this这类名字作为接收者参数名,而是使用短小有意义的名字。
默认都是纯函数
V语言的函数默认是纯函数,也就是函数的输出结果只依赖输入的参数,并且没有其它的副作用。
因为V语言没有全局变量,并且所有的参数默认都是只读的,即使传入的引用也是默认只读的。
然后V语言并不纯的函数式语言。我们可以通过mut关键字让函数参数变得可以被修改:
struct User {
mut:
is_registered bool
}
fn (u mut User) register() {
u.is_registered = true
}
mut user := User{}
println(user.is_registered) // ==> "false"
user.register()
println(user.is_registered) // ==> "true"
在这个例子中,接收者参数u用mut关键字标注为可变的,因此方法内部可以修改user状态。mut也可以用于其它的普通参数:
fn multiply_by_2(arr mut []int) {
for i := 0; i < arr.len; i++ {
arr[i] *= 2
}
}
mut nums := [1, 2, 3]
multiply_by_2(mut nums)
println(nums) // ==> "[2, 4, 6]"
注意,调用函数的时候也必须给nums增加mut关键字。这样可以清楚表达被调用的函数可能要修改这个值。
最好是通过返回值返回结果,而不是修改输入的函数参数。修改参数尽量控制在程序性能比较关键的部分,这样可以即使那分配和复制的开销。
使用 user.register() 或 user = register(user) 代替 register(mut user) 。
V语言可以用简洁的语法返回修改的对象:
fn register(u User) User {
return { u | is_registered: true }
}
user = register(user)
常量
const (
PI = 3.14
World = 'https://vlang-zh.cn'
)
println(PI)
println(World)
常量通过const关键字定义,只能在模块级别定义常量,不能在函数内部定义常量。
常量名必须大写字母开头。这样有助于区别常量和变量。
常量值永远不会被改变。
V语言的常量支持多种类型,甚至是复杂类型的值:
struct Color {
r int
g int
b int
}
fn (c Color) str() string { return '{$c.r, $c.g, $c.b}' }
fn rgb(r, g, b int) Color { return Color{r: r, g: g, b: b} }
const (
Numbers = [1, 2, 3]
Red = Color{r: 255, g: 0, b: 0}
Blue = rgb(0, 0, 255)
)
println(Numbers)
println(Red)
println(Blue)
因为不支持全局的变量,所以支持全局的复杂类型的常量就变得很有必要。
模块
V是一个模块化的语言。它鼓励创建可复用的模块,而且创建模块也很简单。要创建模块需要先创建一个同名的目录,然后里面包含 .v 后缀名的文件:
cd ~/code/modules mkdir mymodule vim mymodule/mymodule.v
// mymodule.v
module mymodule
// To export a function we have to use `pub`
pub fn say_hi() {
println('hello from https://vlang-zh.cn!')
}
在 mymodule 目录下可以有多个v源代码文件。
然后通过 v -lib ~/code/modules/mymodule 命令编译模块。
然后就可以在自己的代码中使用了:
module main
import mymodule
fn main() {
mymodule.say_hi()
}
每次调用模块中的函数必须在函数前面指定模块名。这虽然有点冗长,但是代码更容易阅读和为何,我们一眼就可以看出函数是属于那个模块的。在大型代码库中这很重要。
模块名要短小,一般不要超出10个字符。而且模块也不能出现循环依赖。
所以的模块都将静态编译到单一的可执行程序中。
接口
struct Dog {}
struct Cat {}
fn (d Dog) speak() string {
return 'woof'
}
fn (c Cat) speak() string {
return 'meow'
}
interface Speaker {
speak() string
}
fn perform(s Speaker) {
println(s.speak())
}
dog := Dog{}
cat := Cat{}
perform(dog) // ==> "woof"
perform(cat) // ==> "meow"
类型通过实现的方法满足接口。和Go语言一样,V语言也是隐式接口,类型不需要显式实现接口。
枚举
enum Color {
red green blue
}
mut color := Color.red
// V knows that `color` is a `Color`. No need to use `Color.green` here.
color = .green
println(color) // ==> "1" TODO: print "green"?
可选类型和错误处理
struct User {
id int
name string
}
struct Repo {
users []User
}
fn new_repo() Repo {
return Repo {
users: [User{1, 'Andrew'}, User {2, 'Bob'}, User {10, 'Charles'}]
}
}
fn (r Repo) find_user_by_id(id int) ?User {
for user in r.users {
if user.id == id {
// V automatically wraps this into an option type
return user
}
}
return error('User $id not found')
}
fn main() {
repo := new_repo()
user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks
return // `or` block must end with `return`, `break`, or `continue`
}
println(user.id) // ==> "10"
println(user.name) // ==> 'Charles'
}
V语言针对函数返回值增加了一个可选的属性,这样可以用于处理失败的情况。
将函数升级到可选类型的返回值很简单,只需要给返回值类型增加一个 ? 就可以,这样就可以区别错误和真正的返回值。
如果不需要返回错误信息,可以简单返回Node(TODO:还没有实现)。
这是V语言处理错误的主要手段。函数的返回值依然是值,但是错误处理要简洁很多。
当然,错误还可以继续传播:
resp := http.get(url)? println(resp.body)
http.get 返回的是 ?http.Response 可选类型。如果错误发生,将传播到调用函数,这里是导致main函数抛出异常。
上面代码是下面代码的简写:
resp := http.get(url) or {
panic(err)
}
println(resp.body)
七月的泛型
struct Repo⟨T⟩ {
db DB
}
fn new_repo⟨T⟩(db DB) Repo⟨T⟩ {
return Repo⟨T⟩{db: db}
}
// This is a generic function. V will generate it for every type it's used with.
fn (r Repo⟨T⟩) find_by_id(id int) ?T {
table_name := T.name // in this example getting the name of the type gives us the table name
return r.db.query_one⟨T⟩('select * from $table_name where id = ?', id)
}
db := new_db()
users_repo := new_repo⟨User⟩(db)
posts_repo := new_repo⟨Post⟩(db)
user := users_repo.find_by_id(1)?
post := posts_repo.find_by_id(1)?
为了方便阅读,允许使用 ⟨⟩ 代替 <> 。vfmt最终会将 ⟨⟩ 替换为 <> 。
并发
并发模型和Go语言类似。通过 go foo() 来并发执行 foo() 函数调用。目录每个并发函数运行在独立的系统线程。稍后我们会实现和goroutine类似的调度器。
JSON解码
struct User {
name string
age int
foo Foo [skip] // Use `skip` attribute to skip certain fields
}
data := '{ "name": "Frodo", "age": 25 }'
user := json.decode(User, data) or {
eprintln('Failed to decode json')
return
}
println(user.name)
println(user.age)
JSON是目前流行的格式,因此V语言内置了JSON的支持。
json.decode 解码函数的第一个参数表示要解码的类型,第二个参数是JSON字符串。
V语言会重新生成JSON的编码和解码的代码。因为不使用运行时的反射机制,因此编码和解码的速度都非常快。
单元测试
// hello.v
fn hello() string {
return 'Hello world'
}
// hello_test.v
fn test_hello() {
assert hello() == 'Hello world'
}
所有测试函数都必须放在 *_test.v 文件中,测试函数以 test_ 开头。通过 v hello_test.v 运行单个测试代码,通过 v test mymodule 测试整个模块。
内存管理
V语言没有自动内存回收(GC)和引用计数。V语言会在编译阶段完成必要的清理工作。例如:
fn draw_text(s string, x, y int) {
...
}
fn draw_scene() {
...
draw_text('hello $name1', 10, 10)
draw_text('hello $name2', 100, 10)
draw_text(strings.repeat('X', 10000), 10, 50)
...
}
因为字符串没有从 draw_text 函数逃逸,因此函数调用返回之后就可以被清理。实际上这几个函数调用不会产生任何内存分配的行为。因为两个字符串比较小,V语言会使用提前准备好的缓冲区构造字符串。
对于复杂的情况,目前还需要手工管理内存。但是我们将很快解决这个问题。
V语言运行时会检测内存泄露并报告结果。要释放数组,可以使用 free() 方法:
numbers := [0; 1000000] ... numbers.free()
欢迎关注“网易云课·光谷码农课堂”,V语言入门视频教程!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Go语言中结构体的使用-第2部分OOP
- Go语言中结构体的使用-第1部分结构体
- 用 Go 创建一个新的智能合约语言 - 词法分析器部分
- 木兰编程语言 0.0.14.8:WebSocket 聊天演示;部分比较 Python 语法
- 你负责人工智能哪部分?人工那部分;知识图谱的构建主要靠人工还是机器?
- cocosdx接bugly,上传符号表,有一部分内容解析出来了, 另一部分没有解析出来
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
闪魂FLASH8网站建设实录
马谧铤 / 中国林业 / 2006-7 / 46.00元
《闪魂FLASH8网站建设实录》旨在提供以Flash(Flash 8.0为创作工具)为技术核心的整套互动网站的开发思路,其中包括了网站策划、平面设计、程序设计等实用的互联网应用技术。内容包括Photoshop CS2设计,FIash 8创作和ActionScript应用程序开发的操作流程。在技术学习的过程中.大家还将体会到顶级互动网站设计、网站建设的设计流程和思路。《闪魂FLASH8网站建设实录》......一起来看看 《闪魂FLASH8网站建设实录》 这本书的介绍吧!