Julia - 函数

函数是 Julia 的构建块,是一组指令的集合,用于将参数值元组映射到返回值。 它充当其他编程语言中的子例程、过程、块和其他类似结构概念。

定义函数

我们可以通过以下三种方式定义函数 −

当函数中有单个表达式时,可以通过在左侧括号中写入函数名称和任何参数并在等号右侧写入表达式来定义它。

示例

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

如果一个函数中有多个表达式,可以如下定义 −

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

示例

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)

可选参数

通常可以使用可选参数定义函数,即函数参数的默认合理值,以便在未提供特定值的情况下函数可以使用该值。 例如 −

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

您可以检查上面的输出,当我们调用此函数而不提供第三个值时,变量 cz 默认为 0。

关键字参数

我们定义的某些函数需要大量参数,但调用这些函数可能很困难,因为我们可能会忘记提供参数的顺序。 例如,检查以下函数 −

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

现在,我们可能会忘记参数的顺序,并且可能会发生以下情况 −

foo(“25”, -5.6987, “hello”, 56, good, ‘ABC’)
or
foo(“hello”, 56, “25”, -5.6987, ‘ABC’, good)

Julia 为我们提供了一种避免此问题的方法。 我们可以使用关键字来标记参数。 我们需要在函数的未标记参数后面使用分号,并在其后跟一个或多个 keyword-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", "Tutorialspoint", c=pi, d=22//7)
a is Hello
b is Tutorialspoint
"c => π, d => 22//7"

不必在末尾或匹配位置定义关键字参数,可以将其写在参数列表中的任何位置。 下面是一个例子 −

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

匿名函数

为你的函数想一个很酷的名字是浪费时间。 使用匿名函数,即没有名称的函数。 在 Julia 中,此类函数可以在许多地方使用,例如 map()列表推导式

匿名函数的语法使用符号 ->。 你可以检查下面的例子 −

A -> A^3 + 3A - 3

上面的函数是一个匿名函数,它接受参数 A 并返回 A^3 + 3A – 3

它可以与第一个参数是函数的map()函数一起使用,我们可以定义一个仅针对一个特定map()操作而存在的一次性函数。 下面给出示例 −

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

一旦map()函数完成,函数和参数都会消失 −

 julia> A
ERROR: UndefVarError: A not defined

递归函数

在 Julia 中,函数可以嵌套。 在下面给出的示例中进行了演示 −

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 是一个条件,如果为真则计算 a,否则计算 b。 让我们在下面的递归定义中使用它 −

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

上面的语句计算了直到某个数字(包括某个数字)的所有整数的总和。 但在这个递归中结束是因为有一个基本情况,即当x为1时,返回这个值。

最著名的递归示例是计算第 n 个斐波那契数,它被定义为前两个斐波那契数的总和。 让我们通过下面给出的例子来理解它 −

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

因此,在使用递归时,我们需要小心定义一个基本情况来停止计算。

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

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

通用函数

在 Julia 中,我们看到所有函数本质上都定义为通用。 这意味着这些函数可以用于不同类型的参数。 简而言之,每当使用新类型的参数调用函数时,Julia 编译器都会生成该函数的单独版本。

另一方面,用于特定参数类型组合的函数称为方法。 因此,为了为函数定义新方法(称为重载),我们需要使用相同的函数名但具有不同的参数类型。

多次调度

Julia 有一种名为多重分派的机制,Python 和 C++ 均未实现该机制。 在这种机制下,Julia 将在运行时(每当调用函数时)在 vtable 中进行查找,以根据所有参数的类型找到应该调用哪个现有方法。

让我们借助一个示例来理解多重分派的概念,在该示例中我们将定义一个带有 2 个参数并返回一个字符串的函数。 但在某些方法中,我们会注释两个参数或单个参数的类型。

julia> foo(A, B) = "base case"
foo (generic function with 1 method)

julia> foo(A::Number, B::Number) = "A and B are both numbers"
foo (generic function with 2 methods)

julia> foo(A::Number, B) = "A is a number"
foo (generic function with 3 methods)

julia> foo(A, B::Number) = "B is a number"
foo (generic function with 4 methods)

julia> foo(A::Integer, B::Integer) = "A and B are both integers"
foo (generic function with 5 methods)

我们已经看到它用 5 个方法返回 foo。 当 A 和 B 没有类型时(如基本情况),则它们的类型为 any。

从下面我们可以看到如何选择合适的方法 −

julia> foo(4.5, 20)
"A and B are both numbers"

julia> foo(20, "Hello")
"A is a number"

julia> foo(50, 100)
"A and B are both integers"

julia> foo("Hello", [100,200])
"base case"

多重分派的优点是它永远不会导致错误,因为如果没有其他方法匹配,则肯定会调用基本情况方法。