朱莉娅 - 函数
函数是 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 为我们提供了一种避免这个问题的方法。我们可以使用关键字来标记参数。我们需要在函数的未标记参数后面使用分号,并在其后跟一个或多个关键字值对,如下所示 -
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 形式?甲:乙。它被称为三元,因为它需要三个参数。这里的 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(func, coll)
这里,func是连续应用于集合coll的每个元素的函数。Map通常包含匿名函数并返回一个新集合。下面给出了示例 -
julia> map(A -> A^3 + 3A - 3, [10,3,-2]) 3-element Array{Int64,1}: 1027 33 -17
筛选
过滤器可以定义为采用以下形式的函数 -
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 中,我们看到所有函数本质上都定义为Generic。这意味着这些函数可以用于不同类型的参数。简而言之,每当使用新类型的参数调用函数时,Julia 编译器都会生成该函数的单独版本。
另一方面,用于特定参数类型组合的函数称为Method。因此,为了为函数定义新方法(称为重载),我们需要使用相同的函数名但具有不同的参数类型。
多次调度
Julia 有一种称为 Multiple Dispatch 的机制,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"
多重分派的优点是它永远不会导致错误,因为如果没有其他方法匹配,则肯定会调用基本情况方法。