美文网首页
Vlang官网文档(中文翻译)v0.1.0_20190625

Vlang官网文档(中文翻译)v0.1.0_20190625

作者: 亱_風 | 来源:发表于2019-06-26 00:15 被阅读0次

    PS:主要为自己学习,,,,看的时候顺便翻译的,,,渣翻(非全人工)勿喷.

    介绍

    V是一种用于构建可维护软件的静态类型编译编程语言。

    它与Go相似,也受到Oberon、Rust、Swift的影响。

    V是一种非常简单的语言,阅读这份文档大概只需要半小时的时间,读完之后,您将学习到V的全部内容.

    尽管很简单,但是它为开发人员提供了很多功能,你能用其它编程语言做的任何事情,都可以用V做到.


    Hello World

    fn main() {
        println('hello world')
    }
    

    函数用 fn 声明, 该函数返回值的类型在函数名后定义,在这个例子中,main函数不返回任何内容,所以函数返回值的类型被省略了.

    就像在C语言和所有类C语言中一样,main函数是程序的一个入口点。

    println 是为数不多的内置函数之一。它用于打印值到标准输出。

    fn main() 也可以在程序中被忽略, 这在编写小程序 脚本 或学习语言时非常有用. 为简洁起见, 在这个例子中,将忽略fn main()

    这意味着"hello world"程序可以像下面一样简单

    println('hello world')


    注释

    // 这是一个单行注释
    
    /* 这是一个多行注释
       /* 它可以嵌套 */
    */
    

    函数

    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中一样,函数不能重载,这使代码更简洁,提高了代码的可读性和可维护性.

    函数可以在声明之前被使用,addsub 两个函数的声明在 main之后, 但仍然可以在 main函数中调用. 这在V中的所有声明中都是对的.并且不需要考虑文件和声明的顺序.


    变量

    name := 'Bob' 
    age := 20
    large_number := i64(9999999999)
    println(name)
    println(age)
    println(large_number) 
    

    使用:=将变量声明并初始化赋值,这是在V中声明变量的唯一方法.这意味着被声明的变量总会有一个初始值.

    变量的类型是由给它赋的值判断出来的.要强制使用不同类型的值,请使用类型转换:表达式T(v)将值v转换为类型T。

    与大多数语言不同,V只允许在函数中定义变量。不允许全局(模块级)变量.在V中没有全局状态。

    mut age := 20
    println(age)
    age = 21
    println(age)
    

    要改变变量的值,请使用=. 在V中,变量在默认情况下是不可变的. 要更改变量的值,必须使用mut声明它。

    做个尝试 删除第一行中的mut,然后再编译程序..

    请注意:=和=之间的区别

    :=用于声明和初始化,=用于赋值。

    fn main() {
        age = 21
    } 
    

    这段代码不会编译成功,因为没有声明变量age.在V中,所有变量都需要声明.

    fn main() {
        age := 21
    }
    

    这段代码也不会编译成功,因为声明的变量未使用会导致编译错误。(PS:和Go一样,)

    fn main() {
        a := 10 
        if true {
            a := 20
        } 
    }
    

    不像大多数语言,在V中,用父范围中已经使用的名称声明变量将导致编译错误。


    基本数据类型

    bool 布尔
    
    string  字符串
    
    i8  i16  i32  i64      i128 (soon)
    u8  u16  u32  u64      u128 (soon) 
    
    byte // u8的别名  
    int  // i32的别名  
    rune // i32的别名, 表示Unicode
    
    f32 f64
    
    byteptr
    voidptr
    
    
    

    请注意,与C和Go不同,int 始终是32位整数.


    字符串

    name := 'Bob' 
    println('Hello, $name!')  // `$`用作字符串插值
    println(name.len) 
    
    bobby := name + 'by' // + 用作字符串拼接
    println(bobby) // ==> "Bobby"  
    
    println(bobby.substr(1, 3)) // ==> "ob"  
    // println(bobby[1:3]) // 这个语法很可能替代上一行用的 substr() 函数 
    
    

    在V中, 字符串是一个只读的字节数组.字符串数据使用UTF-8编码,

    单引号和双引号都可以用来表示字符串(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  <-- 这将不会编译成功. `names` 是一个字符串数组. 
    println(names.len) // ==> "3" 
    println('Alex' in names) // ==> "false" 
    
    // 我们也可以预先分配一定数量的元素. 
    nr_ids := 50
    mut ids := [0 ; nr_ids] // 这将创建一个包含50个 0 的数组
    
    

    数组的类型由它的第一个元素决定: [1, 2, 3] 是一个整型数组([]int).
    ['a', 'b'] 是一个字符串数组 ([]string).

    所有的元素必须是相同的类型, [1, 'a'] 是不行滴.

    << 是一个将值追加到数组末尾的操作符.

    .len 字段(field) 返回数组的长度. 注意, 这是一个只读的字段(field), 用户不能修改它. V中所有导出的字段(field)都是只读的。.

    val in array 如果数组中包含val的时候,返回true.


    Maps

    mut m := map[string]int{} // 目前只允许使用字符串类型的key
    m['one'] = 1
    println(m['one']) // ==> "1"  
    println(m['bad_key']) // ==> "0"  
    // TODO: 实现检查key是否存在的方法 
    
    numbers := { // TODO:此语法尚未实现  
        '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 会优化这些表达式, 让上面的两个if语句都生成相同的机器码,不创建数组。


    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')  // 输出: 0) Sam
    }                         //       1) Peter
    
    

    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 方式更安全, 因为使用后者很容易忘记更新计数器,陷入死循环.

    这的 i 这里不需要用 mut 声明,因为根据定义它总是可变的。


    switch语句

    os := 'windows' 
    print('V is running on ')
    switch os {
    case 'darwin':
        println('macOS.')
    case 'linux':
        println('Linux.')
    default:
        println(os) 
    }
    // TODO: 用匹配表达式代替 
    
    

    switch语句用于在条件多的时候替代if - else 语句. 当遇到第一个case满足时,即运行,运行完后不再向下匹配.

    与C不同, 每个语句块后不需要break 语句.


    struct 结构体

    struct Point {
        x int
        y int 
    } 
    
    p := Point{
        x: 10 
        y: 20 
    } 
    println(p.x) // 结构体的字段使用点 . 访问 
    
    

    结构体是在堆栈上分配的。要在堆上分配一个结构并获得指向它的指针,使用&前缀:

    pointer := &Point{10, 10}  // 具有3个或更少字段的结构,可选初始化语法
    println(pointer.x) // 指针有相同的语法访问字段
    
    

    V 没有子类, 但它支持嵌入式结构

    // TODO: 这将在6月晚些时候实现
    struct Button {
        Widget
        title string
    }
    
    button := new_button('Click me')
    button.set_pos(x, y)
    
    // 没有嵌入,我们就得这么做
    button.widget.set_pos(x,y)
    
    

    访问修饰符

    默认情况下,struct的字段是私有不可变的(同时也使Struct不可变),可以用它们的pub和mut访问修饰符更改,总共有五种选项:

    struct Foo {
        a int     // 私有不可变的 (默认) 
    mut: 
        b int     // 私有可变的
        c int     // (您可以列出具有相同访问修饰符的多个字段)   
    pub: 
        d int     // 公共不可变 (只读) 
    pub mut: 
        e int     // 公共, 但仅在父模块中可变  
    pub mut mut: 
        f int     // public 父模块的内部和外部都是可变的 
    }                 // (不推荐使用,这就是它如此冗长的原因) 
    
    

    例如,这是在内置模块中定义的字符串类型:

    struct string {
        str byteptr
    pub:
        len int
    }
    
    

    从这个定义很容易看出string是一个不可变类型。

    指向字符串数据的字节指针在内置之外根本无法访问,len字段是公共的,但不是可变的(只读的).

    fn main() {
        str := 'hello' 
        len := str.len // OK,一切正常  
        str.len++      // 编译错误(因为该字段是只读的)  
    } 
    
    

    方法

    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 方法有一个被叫做 uUser类型接收器 . 惯例是不要使用self或this这样的接收方名称,而是使用一个简短的, 最好是一个字母的名称.


    默认纯函数Pure functions by default

    默认情况下,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"  
    
    

    在这个例子中,接收器(第一个参数)被标记为可变的,所以register()可以改变user对象,对于非接收器参数也是如此:

    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前面加上mnt,这表明被调用的函数将修改值。

    最好返回值而不是修改参数(最好是调用函数返回),修改参数应该只在应用程序的性能关键部分执行,以减少分配和复制。

    使用 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 = '世界'
    ) 
    
    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不允许全局变量,所以这非常有用


    模块Modules

    V是一种非常模块化的语言,鼓励创建可重用模块,而且非常简单.要创建一个新模块,请创建一个模块名称的目录,在目录中创建包含代码的.v文件:

    cd ~/code/modules
    mkdir mymodule
    vim mymodule/mymodule.v
    
    
    // mymodule.v
    module mymodule
    
    // 要导出的函数必须使用`pub`修饰符
    pub fn say_hi() {
        println('hello from mymodule!')
    }
    
    

    您可以在mymodule/中创建任意数量的.v文件。

    使用 v -lib ~/code/modules/mymodule生成它

    就这样,你现在可以在你的代码中使用它:

    module main
    
    import mymodule
    
    fn main() {
        mymodule.say_hi()
    }
    
    

    注意,每次调用外部函数时必须指定模块.乍一看,这似乎有些冗长,但它使代码更易于阅读和理解.因为总是很清楚从哪个模块调用哪个函数,特别是在大型代码库中。

    模块名称应该简短,不超过10个字符。不允许循环导入。

    您可以在任何地方创建模块。

    所有模块都被静态地编译成一个可执行文件。


    接口Interfaces

    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" 
    
    

    类型通过实现其方法来实现接口。没有明确的意图声明,没有“implementation”关键字。


    枚举Enums

    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"?  
    
    

    选项/结果 类型 &错误处理 Option/Result types & error handling

    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将选项和结果组合成一种类型,所以您不需要决定使用哪种类型。

    将一个函数“升级”为一个可选函数所需的工作量很小:您必须添加一个?返回类型,当出现错误时返回一个错误。

    如果不需要返回错误,可以简单地返回None(TODO:然而None还没实现)

    这是在v中处理错误的主要方法。它们仍然是值,就像在Go中一样,但是优点是错误不能被取消处理,而且处理它们要少很多麻烦。

    你也可以这样传错误信息

    resp := http.get(url)?
    println(resp.body)
    
    

    http.get 返回 ?http.Response,返回?,所以错误会被传递到调用函数的函数

    上面的代码基本是:

    resp := http.get(url) or {
        panic(err)
    }
    println(resp.body)
    
    

    泛型(7月)Generics(July)

    struct Repo⟨T⟩ {
        db DB
    }
    
    fn new_repo⟨T⟩(db DB) Repo⟨T⟩ {
        return Repo⟨T⟩{db: db}
    }
    
    // 这是一个泛型函数. V将为它所使用的每种类型生成它。 
    fn (r Repo⟨T⟩) find_by_id(id int) ?T {  
        table_name := T.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自动替换< >⟨⟩。


    并发性Concurrency

    并发模型与Go非常相似。要同时运行foo(),只需用go foo()调用它。现在,它在一个新的系统线程中启动该函数。很快goroutines和调度程序将被实现。


    Decoding 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现在非常流行,这就是内置JSON支持的原因。

    decode函数的第一个参数是要解码的类型。第二个参数是JSON字符串。

    V生成用于JSON编码和解码的代码。不使用运行时反射。这导致了更好的性能。


    测试Testing

    // hello.v 
    fn hello() string {
        return 'Hello world'
    } 
    
    // hello_test.v 
    fn test_hello() {
        assert hello() == 'Hello world'
    } 
    
    

    所有的测试函数都以test_开头必须放在*_test.v文件中。要运行测试,请执行 v hello_test.v. 要测试整个模块, 运行 v test mymodule.


    内存管理Memory management

    没有垃圾收集或引用计数。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()
    
    

    Defer

    TODO

    vfmt

    TODO

    高级主题


    V中调用C函数

    #flag -lsqlite3
    
    #include "sqlite3.h"
    
    struct C.sqlite3 
    struct C.sqlite3_stmt 
    
    fn C.sqlite3_column_int(C.sqlite_stmt, int) int 
    
    fn main() {
        path := 'sqlite3_users.db' 
        db := &C.sqlite3{} 
        C.sqlite3_open(path.cstr(), &db)
    
        query := 'select count(*) from users' 
        stmt := &C.sqlite3_stmt{} 
        C.sqlite3_prepare_v2(db, query.cstr(), - 1, &stmt, 0)
        C.sqlite3_step(stmt) 
        nr_users := C.sqlite3_column_int(res, 0)
        C.sqlite3_finalize(res)
        println(nr_users) 
    } 
    
    

    Compile time if

    $if windows {
        println('Windows')  
    }  
    $if linux {
        println('Linux') 
    } 
    $if mac {
        println('macOS') 
    } 
    
    

    编译时if 使用 $. 现在它只能用来检测操作系统.


    相关文章

      网友评论

          本文标题:Vlang官网文档(中文翻译)v0.1.0_20190625

          本文链接:https://www.haomeiwen.com/subject/ohnfcctx.html