函数(Function Types)

函数类型1即是函数这种特殊的类型。

  • 可以将一个函数赋值给一个变量,一个函数类型的变量。
  • 还可以将一个函数作为参数进行传递。
  • 也可以在函数调用中返回一个函数。

函数类型有两类;可分为internalexternal函数。

内部函数(internal)

因为不能在当前合约的上下文环境以外的地方执行,内部函数只能在当前合约内被使用。如在当前的代码块内,包括内部库函数,和继承的函数中。

外部函数(External)

外部函数由地址和函数方法签名两部分组成。可作为外部函数调用的参数,或者由外部函数调用返回。

函数的定义

完整的函数的定义如下:


function (<parameter types>) {internal(默认)|external} [constant] [payable] [returns (<return types>)]

若不写类型,默认的函数类型是internal的。如果函数没有返回结果,则必须省略returns关键字。下面我们通过一个例子来了解一下。

pragma solidity ^0.4.0;

contract Test{
    //默认是internal类型的
    function noParameter() returns (uint){}

    //无返回结果
    function noReturn1(uint x) {}

    //如果无返回结果,必须省略`returns`关键字
    //function noReturn2(uint x) returns {} 
}

如果一个函数变量没有初始化,直接调用它将会产生异常。如果delete了一个函数后调用,也会发生同样的异常。

如果外部函数类型在Solidity的上下文环境以外的地方使用,他们会被视为function类型。编码为20字节的函数所在地址,紧跟4字节的函数方法签名2的共占24字节的bytes24类型。

合约中的public的函数,可以使用internalexternal两种方式来调用。下面来看看,两种方式的不同之处。

函数的internalexternal

调用一个函数f()时,我们可以直接调用f(),或者使用this.f()。但两者有一个区别。前者是通过internal的方式在调用,而后者是通过external的方式在调用。请注意,这里关于this的使用与大多数语言相背。下面通过一个例子来了解他们的不同:

pragma solidity ^0.4.5;

contract FuntionTest{
    function internalFunc() internal{}

    function externalFunc() external{}

    function callFunc(){
        //直接使用内部的方式调用
        internalFunc();

        //不能在内部调用一个外部函数,会报编译错误。
        //Error: Undeclared identifier.
        //externalFunc();

        //不能通过`external`的方式调用一个`internal`
        //Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest
        //this.internalFunc();

        //使用`this`以`external`的方式调用一个外部函数
        this.externalFunc();
    }
}
contract FunctionTest1{
    function externalCall(FuntionTest ft){
        //调用另一个合约的外部函数
        ft.externalFunc();
        
        //不能调用另一个合约的内部函数
        //Error: Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest
        //ft.internalFunc();
    }
}

函数例子(官方)

pragma solidity ^0.4.0;

library ArrayUtils{
    function range(uint length) internal returns (uint[] memory r){
        r = new uint[](length);
        
        for(uint i = 0; i < length; i++){
            r[i] = i;
        }
    }
    
    function map(uint[] memory self, 
        function(uint) returns (uint) f
    ) 
        internal 
        returns (uint[] memory r)
    {
            r = new uint[](self.length);
            for(uint i = 0; i < self.length; i++){
                r[i] = f(self[i]);
            }
    }
    
    function reduce(uint[] memory self,
        function(uint x, uint y) returns(uint) f
    )
        internal
        returns (uint r)
    {
        r = self[0];
        for(uint i = 1; i < self.length; i++){
            r = f(r, self[i]);
        }
    }
}

contract Pyramid{
    using ArrayUtils for *;
    
    function pryamid(uint length) returns (uint){
        return ArrayUtils.range(length).map(square).reduce(sum);
    }
    
    function square(uint x) returns (uint){
        return x * x;
    }
    
    function sum(uint x, uint y) returns (uint){
        return x + y;
    }
}

Question?

  • library是什么呢。
  • library引入时为什么使用using,这和文件引入的import有何区别。
  • library内的函数全是internal的。
  • library内的函数,他的参数函数为什么是internal的,不应该是external的?
  • uint[]是什么类型,不能写做[]uint
  • memory又是什么呢,为什么map函数明明是两个参数,但只需要传一个呢。

  1. http://solidity.readthedocs.io/en/develop/types.html#function-types 

  2. 函数签名的编码方式可查看函数选择器相关章节,【文档翻译系列】ABI详解 

处于某些特定的环境下,可以看到评论框,欢迎留言交流^_^。