朱莉娅 - 函数


函数是 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"

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