Scala笔记(1)——一个入门的Scala程序

这是一个简单的入门Scala程序(iterate.scala):

object Iterate {
  def main (args: Array[String]) {
    args.foreach(s => println(s))
  }
}

在程序中,Iterate被定义成objectScalaobjectsingleton,也就是运行时只能产生一个Iterate实例。

main方法只能定义在object中,传入到main方法中的命令行参数是一个字符串数组:args: Array[String]main方法可以看成是Java类的static main方法,即Iterate程序的入口函数。

s => println(s)是一个functional literal,做的仅仅是打印s而已。

执行这个程序:

[root@Fedora scala]# scala iterate.scala  1 2
1
2

可以看出打印了命令行参数。

如何运行scala脚本

搭建Scala开发环境一文中可以看到不加任何参数直接运行scala命令时,会启动REPL(Read,Eval,Print,Loop)环境,然后就可以在这个环境里交互地执行Scala代码。本文介绍如何运行Scala脚本(script)。

首先创建一个简单的Scala脚本(hello.scala):

print("Hello world!\n")

a)直接运行scala hello.scala

[root@Fedora scala]# scala hello.scala
Hello world!

b)在REPL环境用load命令运行hello.scala

[root@Fedora scala]# scala
Welcome to Scala version 2.10.4 (OpenJDK 64-Bit Server VM, Java 1.8.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala> :load hello.scala
Loading hello.scala...
Hello world!

c)如果想把脚本编译成JVM byte code (一组*.class文件),可以使用scalac程序(命令行选项是-Xscript <object><object>是“main class”的名字,也即Java程序的入口):

[root@Fedora scala]# scalac -Xscript hello hello.scala
[root@Fedora scala]# ls
hello$$anon$1.class  hello.class  hello$.class  hello.scala
[root@Fedora scala]# scala hello
Hello world!

也可以用scalap这个逆向分析工具分析一下hello.class

[root@Fedora scala]# scalap -cp . hello
object hello extends scala.AnyRef {
  def this() = { /* compiled code */ }
  def main(argv : scala.Array[scala.Predef.String]) : scala.Unit = { /* compiled code */ }
}

Lua笔记(7)——luaL_loadfile和luaL_dofile的区别

luaL_loadfile()会加载和编译Lua脚本,但不会运行。而luaL_dofile不仅运行编译后的脚本,运行结束后还会把脚本pop出栈。看下面这个例子:

一个简单的脚本(test.lua):

print "Hello World!"

首先看调用luaL_loadfile()的程序:

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static void stackDump(lua_State *L)
{
    int i = 0;
    int type = 0;
    int top = lua_gettop(L);

    printf("There is %d elements in stack: ", top);
    for (i = 1; i <= top; i++)
    {
        type = lua_type(L, i);
        switch (type)
        {
            case LUA_TSTRING:
            {
                printf("'%s'", lua_tostring(L, i));
                break;
            }
            case LUA_TBOOLEAN:
            {
                printf(lua_toboolean(L, i) ? "true" : "false");
                break;
            }
            case LUA_TNUMBER:
            {
                printf("%g", lua_tonumber(L, i));
                break;
            }
            default:
            {
                printf("Element type is %s", lua_typename(L, type));
                break;
            }
        }
        printf(" ");
    }
    printf("\n");
    return;
}

static void bail(lua_State *L)
{
    fprintf(stderr, "\nFATAL ERROR:%s\n\n", lua_tostring(L, -1));
    exit(1);
}
int main(void)
{   
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    if (luaL_loadfile(L, "test.lua"))
    {
        bail(L);
    }

    stackDump(L);

    lua_close(L);
    return 0;
}

执行结果:

[root@Fedora test]# ./a
There is 1 elements in stack: Element type is function

可以看到,并没有打印“Hello World!”,而且栈里还有一个类型为function的元素。

接下来看调用luaL_dofile()的程序:

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static void stackDump(lua_State *L)
{
    int i = 0;
    int type = 0;
    int top = lua_gettop(L);

    printf("There is %d elements in stack: ", top);
    for (i = 1; i <= top; i++)
    {
        type = lua_type(L, i);
        switch (type)
        {
            case LUA_TSTRING:
            {
                printf("'%s'", lua_tostring(L, i));
                break;
            }
            case LUA_TBOOLEAN:
            {
                printf(lua_toboolean(L, i) ? "true" : "false");
                break;
            }
            case LUA_TNUMBER:
            {
                printf("%g", lua_tonumber(L, i));
                break;
            }
            default:
            {
                printf("Element type is %s", lua_typename(L, type));
                break;
            }
        }
        printf(" ");
    }
    printf("\n");
    return;
}

static void bail(lua_State *L)
{
        fprintf(stderr, "\nFATAL ERROR:%s\n\n", lua_tostring(L, -1));
        exit(1);
}
int main(void)
{
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    if (luaL_dofile(L, "test.lua"))
    {
        bail(L);
    }

    stackDump(L);

    lua_close(L);
    return 0;
}

执行结果:

[root@Fedora test]# ./a
Hello World!
There is 0 elements in stack:

可以看到,不仅打印了“Hello World!”,而且栈也变成了空。

参考资料:
lual_dofile(); wont load script with C++ and Lua

Lua笔记(6)——栈(stack)

C程序和Lua库之间通过栈(stack)来进行数据交换,并且栈中的每个槽位(slot)都能存放任意的Lua数据类型值。栈如下图所示:

                            |________|    <--  (-1)
                            |________|    <--  (-2)
                            |________|
                            | ...... |
                            |________|    <--  (2)
                            |________|    <--  (1)

栈底以1为起始索引,而栈顶则以-1作为起始索引。lua_gettop()函数可以返回当前栈中的元素个数,同时也是栈顶元素的索引值。如果是空栈,则 lua_gettop()返回0

Lua C API的核心就集中在对栈的操作上:当想要运行某个Lua脚本时,需要调用luaL_dofile()函数。而想执行Lua脚本的某个函数时,则首先要把函数push进栈(lua_getglobal()),如果函数需要参数,则参数也要相应地进栈(lua_pushXX())。接下来执行函数(比如lua_pcall()函数)。当函数退出时,返回值同样push进栈,C程序就可以使用lua_toXX()函数获得结果。请看下面这个例子:

一个简单的Lua脚本(test.Lua):

function add (a, b)
    return (a+b)
end

C程序如下:

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static void bail(lua_State *L)
{
    fprintf(stderr, "\nFATAL ERROR:%s\n\n", lua_tostring(L, -1));
    exit(1);
}
int main(void)
{   
    lua_State *L = luaL_newstate();

    luaL_openlibs(L);

    if (luaL_dofile(L, "test.lua"))
    {
        bail(L);
    }

    lua_getglobal(L, "add");


    lua_pushnumber(L, 1);
    lua_pushnumber(L, 2);

    if (lua_pcall(L, 2, 1, 0))
    {
        bail(L);
    }

    printf("%g\n", lua_tonumber(L, -1));

    lua_close(L);
    return 0;
}

执行结果如下:

3

Go语言数组浅析

先看一个简单的数组定义:

var array [3]int

上面语句定义了变量array,这个变量是一个包含3个整数的数组类型。可以使用Go语言内置的len函数得到数组长度:

package main

import (
    "fmt"
)

func main() {
    var array [3]int
    fmt.Println(len(array))
}

执行以上程序结果:

3

需要注意的是,在Go语言中,数组的长度是数组类型的一部分,它必须是一个常量表达式,即长度的值在程序编译时是可以得到的。请看下面这个程序:

package main

import (
    "fmt"
)

func print_array(array []int) {
    fmt.Printf("In function, array is %v\n", array)
}

func main() {
    var array [3]int

    print_array(array )
}

编译这个程序会产生下面的错误:

main.go:14: cannot use array (type [3]int) as type []int in argument to print_array

Go语言要求传入函数的形参和实参的类型必须是完全匹配的,而array的类型是[3]int,不是[]int,所以编译会出错。

Go语言函数参数都是传值调用,所以如果函数的参数是数组的话,那么实际传入的将是原数组的拷贝。请看这个例子:

package main

import (
    "fmt"
)

func change_array(array [3]int) {
    for i, _ := range array {
        array[i] = 1
    }
    fmt.Printf("In function, array address is %p, value is %v\n", &array, array)
}

func main() {
    var array [3]int

    fmt.Printf("Original array address is %p, value is %v\n", &array, array)
    change_array(array)
    fmt.Printf("Changed array address is %p, value is %v\n", &array, array)
}

执行结果:

Original array address is 0xc082006560, value is [0 0 0]
In function, array address is 0xc0820065e0, value is [1 1 1]
Changed array address is 0xc082006560, value is [0 0 0]

可以看到,在change_array函数中操作的数组地址并不是main函数中的数组地址,而main函数中的数组的值在调用change_array函数前后也没有发生变化。

如果数组的元素类型是可比较的,那么数组类型就是可比较的,即可以使用“==”和“!=”运算符对数组进行运算。 请看下例:

package main
import "fmt"

func main()  {
    a := [...]int{2, 1}
    b := [...]int{1, 2}
    fmt.Println(a == b, a != b)
}

执行结果如下:

false true

 

如何编译使用Lua库的C程序

编写使用LuaC程序后,除了需要链接Lua库以外,还需要链接libmlibdl。以下面程序为例(test.c):

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

int main(void)
{
    char buf[256] = {0};
    int error = 0;

    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    while (fgets(buf, sizeof(buf), stdin) != NULL)
    {
        error = luaL_loadstring(L, buf) || lua_pcall(L, 0, 0, 0);
        if (error)
        {
            fprintf(stderr, "Error is %s.\n", lua_tostring(L, -1));
            lua_pop(L, 1);
        }
    }
    lua_close(L);
    return 0;
}

编译:

[root@Fedora test]# gcc -g -o test test.c -llua -lm -ldl

执行:

[root@Fedora test]# ./test
print("hello")
hello
^C
[root@Fedora test]#

什么是coroutine(协程)?

Coroutine isn't thread!(协程不是线程!)

Coroutinethread有许多共同点:每个coroutine(或thread)都有一组指令执行序列,私有的栈,私有的局部变量,私有的程序计数器(program counter);同时各个coroutine(或thread)之间共享全局变量等等信息。

Coroutinethread的不同在于:在多核系统上,一个进程的多个thread是可以并行运行的(in parallel),即在一个时间点上,多个thread同时在运行。而coroutine之间是合作式的(collaborative),在任何一个时间点,只有一个coroutine在运行。Coroutine之间的调度是非抢占式的(non-preemptive):只有运行的coroutine主动地放弃执行权,其它coroutine才可以获得执行机会。

“Orthogonality”的含义

Orthogonality(正交性)的含义通俗来讲是指“改变A不会改变B”。一个“Orthogonality”系统的例子是收音机:换台不会改变音量;而不是“Orthogonality”系统的例子则是直升飞机:改变速度也会改变方向。

在编程语言中,Orthogonality指执行一条指令时,只有这条指令被执行,不会有其它任何副作用(side-effect)。

参考资料:
What is “Orthogonality”?

Lua笔记(5)——C API头文件简介

搭建Lua环境时,安装程序会把以下5个头文件拷贝到指定目录(默认为/usr/local/include/):

-rw-r--r--.  1 root root 14825 Jun  3 09:03 lua.h
-rw-r--r--.  1 root root 20581 May 20 13:39 luaconf.h
-rw-r--r--.  1 root root  8433 Oct 29  2014 lauxlib.h
-rw-r--r--.  1 root root  1173 Feb  6  2014 lualib.h
-rw-r--r--.  1 root root   191 Dec 22  2004 lua.hpp

先从最简单的lua.hpp来看:

// lua.hpp
// Lua header files for C++
// <<extern "C">> not supplied automatically because Lua also compiles as C++

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

可以看到lua.hpp只是包含了其它的几个头文件,供C++程序使用。

lua.h定义了Lua提供的最基本的功能:创建Lua环境,调用Lua函数,在Lua环境中读写全局变量等。lua.h定义的所有成员(包括函数,宏等)都是用lua_LUA_作为前缀。

lauxlib.h定义了Lua辅助库(auxiliary library)提供的的函数,这个文件里定义的成员都是以luaL_作为前缀。Lua辅助库在lua.h定义的基础API之上,又封装了一层API,以提供更好的通用性。Lua辅助库并不会访问Lua的内部,所有操作都是通过lua.h的基础API去操作。

luaconf.h会定义Lua环境的配置信息。

lualib.h定义了Lua语言标准库的名字,以及打开这些库的函数。