Julia 函数

函数是一组一起执行一个任务的语句。

在 Julia 里,函数是将参数值组成的元组映射到返回值的一个对象。

Julia 中使用 function 定义函数,基本语法是:

function functionname(args)
   expression
   expression
   expression
   ...
   expression
end

默认情况下,函数返回的值是最后计算的表达式的值,所以我们看到上面是没有 return 语句的,当然,如果使用 return 关键字,函数就会立即返回:。

julia>function f(x,y)
           x + y
       end
f (generic function with 1 method)
julia>f(2,3)
5

julia>function bills(money)
      if money < 0
         return false
      else
         return true
      end
   end
bills (generic function with 1 method)

julia>bills(50)
true

julia>bills(-50)
false

如果函数要返回多个值,可以使用元组:

julia>function mul(x,y)
                  x+y, x*y
               end
mul (generic function with 1 method)

julia> mul(5, 10)
(15, 50)

当函数中只有一个表达式时,您可以省略 function 关键字,等号左侧设置函数名与参数,右侧设置表达式,类似赋值形式:

julia>f(x,y) = x + y
f (generic function with 1 method)

没有括号时,表达式 f 指的是函数对象,可以像任何值一样被传递:

julia>g = f;
julia>g(2,3)
5
julia>f(a) = a * a f (generic function with 1 method) julia> f(5) 25 julia> func(x, y) = sqrt(x^2 + y^2) func (generic function with 1 method) julia> func(5, 4) 6.4031242374328485

和变量名一样,Unicode 字符也可以用作函数名:

julia> ∑(x,y) = x + y
∑ (generic function with 1 method)

julia> ∑(2, 3)
5

返回类型

我们可以使用 :: 运算符来指定函数的返回类型。

julia>function g(x, y)::Int8
           return x * y
       end;

 julia>typeof(g(1, 2))
Int8

以上函数范例将忽略 x 和 y 的类型,返回 Int8 类型的值。

可选参数

在函数中我们可以设置参数默认值,这样在没有提供该参数的时候,就可以使用默认值来计算:

以下范例定义函数 pos,设置三个参数,其中参数 cz 设置默认值为 0,在函数调用时,可以不提供该参数:

 julia>function pos(ax, by, cz=0)
         println("$ax, $by, $cz")
      end
pos (generic function with 2 methods)

 julia>pos(10, 30)
10, 30, 0

 julia> pos(10, 30, 50)
10, 30, 50

关键字参数

有时候我们定义的一些函数需要大量参数,但调用这些函数可能很麻烦,因为我们可能会忘记提供参数的顺序。 例如:

function foo(a, b, c, d, e, f) ... end

我们可能会忘记参数的顺序,发生以下调用函数的情况:

foo("25", -5.6987, "hello", 56, good, 'ABC')
或
foo("hello", 56, "25", -5.6987, 'ABC', good)

这样看起来就非常混乱。

Julia 关键字参数允许通过名称而不是仅通过位置来识别参数,使得这些复杂函数易于使用和扩展。

使用关键字来标记参数,需要在函数的未标记参数之后使用分号 ;,并在其后跟一个或多个关键值对 key=value,如下所示:

 julia> function foo(a, b ; c = 10, d = "hi")
         println("a is $a")
         println("b is $b")
         return "c => $c, d => $d"
      end
foo (generic function with 1 method)

 julia> foo(100,20)
a is 100
b is 20
"c => 10, d = > hi"

 julia> foo("Hello", "Codebaoku", c=pi, d=22//7)
a is Hello
b is Codebaoku
"c => π, d => 22//7"

使用关键字参数,参数的位置也不太重要了,以上函数我们也可以这样调用:

julia> foo(c=pi, d =22/7, "Hello", "Codebaoku") a is Hello b is Runoob "c => π, d => 3.142857142857143"

 

1. 匿名函数

匿名函数是一个没有函数名的函数。

匿名函数在程序运行时动态声明,除了没有函数名外,其他的与标准函数一样。

在 Julia 中,匿名函数可用于许多地方,例如 map() 和 列表推导。

使用匿名函数后,我们的代码变得更简洁了。

匿名函数的语法使用符号 ->。

 julia> x -> x^2 + 2x - 1
#1 (generic function with 1 method)

julia> function (x)
           x^2 + 2x - 1
       end
#3 (generic function with 1 method)
 

以上范例创建了一个接受一个参数 x 并返回当前值的多项式 x^2+2x-1 的函数。

匿名函数最主要的用法是传递给接收函数作为参数的函数。一个经典的例子是 map ,为数组的每个元素应用一次函数,然后返回一个包含结果值的新数组:

julia> map(round, [1.2, 3.5, 1.7])
3-element Vector{Float64}:
 1.0
 4.0
 2.0

如果做为第一个参数传递给 map 的转换函数已经存在,那直接使用函数名称是没问题的。但是通常要使用的函数还没有定义好,这样使用匿名函数就更加方便:

julia> map(x -> x^2 + 2x - 1, [1, 3, -1])
3-element Vector{Int64}:
  2
 14
 -2

接受多个参数的匿名函数写法可以使用语法 (x,y,z)->2x+y-z,而无参匿名函数写作 ()->3 。无参函数的这种写法看起来可能有些奇怪,不过它对于延迟计算很有必要。这种用法会把代码块包进一个无参函数中,后续把它当做 f 调用。

例如,考虑对 get 的调用:

get(dict, key) do
    # default value calculated here
    time()
end

上面的代码等效于使用包含代码的匿名函数调用 get。 被包围在 do 和 end 之间,如下所示:

get(()->time(), dict, key)

这里对 time 的调用,被包裹了它的一个无参数的匿名函数延迟了。该匿名函数只当 dict 缺少被请求的键时,才被调用。

 

2. 函数嵌套与递归

在 Julia 中,函数可以嵌套使用。

以下范例 add() 函数内嵌套一个 add1 范例:

julia> function add(x)
         Y = x * 2
         function add1(Y)
           Y += 1
         end
         add1(Y)
       end
add (generic function with 1 method)

julia> d = 10
10

julia> add(d)
21

同样,Julia 中的函数也可以是递归的。

递归指的是在函数的定义中使用函数自身的方法。

举个例子: 从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?"从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?'从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……'"

以下我们使用三元运算符来测试递归,三元运算符操作三个操作对象 expr ? a : b,如果 expr 为 true ,值为 a 的计算结果 ,否则为 b 的计算结果。

julia> sum(x) = x < 1 ? sum(x-1) + x : x
sum (generic function with 1 method)

julia> sum(10)
55

以上范例用于计算某个整数之前所有数的总和,直到并包括某个数字。 在这个递归中,因为有一个基本情况,即当 x 为 1 时,这个值被返回。

递归最著名的例子是计算第 n 个斐波那契数,斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........

这个数列从第 3 项开始,每一项都等于前两项之和。

julia>  fib(x) = x < 2 ? x : fib(x-1) + fib(x-2)
fib (generic function with 1 method)

julia>  fib(10)
55

julia> fib(20)
6765

 

3. Map

Map 定义格式如下:

map(func, coll)

这里,func 是一个函数,它依次应用于集合 coll 的每个元素。 Map 一般包含匿名函数并返回一个新的集合。

julia> map(A ->  A^3 + 3A - 3, [10,3,-2])
3-element Array{Int64,1}:
 1027
   33
  -17

 

4. Filter

Filter 定义格式如下:

filter(function, collection)

filter 函数返回集合的副本,并删除通过调用该函数结果为 false 的元素。

julia> array = Int[1,2,3]
3-element Array{Int64,1}:
 1
 2
 3
 
julia> filter(x -> x % 2 == 0, array)
1-element Array{Int64,1}:
 2