跳转到内容

Julia/函数简介

来自维基教科书,开放世界中的开放书籍
Previous page
控制流程
Julia 简介 Next page
字典和集合
函数

函数是 Julia 代码的基本构建块,充当其他编程语言中发现的子例程、过程、块和类似的结构概念。

函数是收集在一起的一组指令,可以返回一个或多个值,可能基于输入参数。如果参数包含可变的值(如数组),则数组可以在函数内部修改。按照惯例,函数名称末尾的感叹号(!)表示函数可能会修改其参数。

定义函数有各种语法

  • 当函数包含单个表达式时
  • 当函数包含多个表达式时
  • 当函数不需要名称时

单表达式函数

[编辑 | 编辑源代码]

要定义一个简单的函数,您只需在等号的左侧提供函数名称和任何参数(用括号括起来),在等号的右侧提供一个表达式。这就像数学函数一样

julia> f(x) = x * x
f (generic function with 1 method)

julia> f(2)
4
julia> g(x, y) = sqrt(x^2 + y^2)
g (generic function with 1 method)

julia> g(3, 4)
5.0

多表达式函数

[编辑 | 编辑源代码]

定义包含多个表达式的函数的语法如下

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

这是一个典型的函数,它调用另外两个函数,然后结束。

function breakfast()
   maketoast()
   brewcoffee()
end

breakfast (generic function with 1 method)

最终表达式(此处为 brewcoffee() 函数)返回的值也是 breakfast() 函数返回的值。

您可以使用 return 关键字来指示要返回的特定值

julia> function canpaybills(bankbalance)
    if bankbalance < 0
       return false
    else
       return true
    end
end
canpaybills (generic function with 1 method)
julia> canpaybills(20)
true
 
julia> canpaybills(-10)
false

有些人认为始终使用 return 语句是一种良好的风格,即使它不是严格必要的。稍后我们将看到如何确保当您使用错误类型的参数调用函数时,函数不会偏离轨道。

从函数返回多个值

[编辑 | 编辑源代码]

要从函数返回多个值,请使用元组(在后面的章节中更详细地探讨)。

function doublesix()
    return (6, 6)
end
doublesix (generic function with 1 method)
julia> doublesix()
(6, 6)

这里您可以编写 6, 6 而不使用括号。

可选参数和可变参数数量

[编辑 | 编辑源代码]

您可以定义带有可选参数的函数,这样如果未提供特定值,函数可以使用合理的默认值。您在参数列表中提供默认符号和值

function xyzpos(x, y, z=0)
    println("$x, $y, $z")
end
xyzpos (generic function with 2 methods)

当您调用此函数时,如果您没有提供第三个值,则变量 z 默认值为 0,并在函数内部使用该值。

julia> xyzpos(1,2)
1, 2, 0
julia> xyzpos(1,2,3)
1, 2, 3

关键字参数和位置参数

[编辑 | 编辑源代码]

当您编写具有很长参数列表的函数时,如下所示

function f(p, q, r, s, t, u)
...
end

迟早您会忘记必须按什么顺序提供参数。例如,它可能是

f("42", -2.123, atan2, "obliquity", 42, 'x')

f(-2.123, 42, 'x', "42", "obliquity", atan2)

您可以通过使用关键字标记参数来避免此问题。在函数的未标记参数后使用分号,然后在其后加上一个或多个 keyword=value

function f(p, q ; r = 4, s = "hello")
  println("p is $p")
  println("q is $q")
  return "r => $r, s => $s"
end
f (generic function with 1 method)

当调用时,此函数需要两个参数,还接受一个数字和一个字符串,分别标记为 rs。如果您没有提供关键字参数,则使用它们的默认值

julia> f(1,2)
p is 1
q is 2
"r => 4, s => hello"

julia> f("a", "b", r=pi, s=22//7)
p is a
q is b
"r => π = 3.1415926535897..., s => 22//7"

如果您提供了关键字参数,它可以在参数列表中的任何位置,而不仅仅是在末尾或匹配位置。

julia> f(r=999, 1, 2)
p is 1
q is 2
"r => 999, s => hello"

julia> f(s="hello world", r=999, 1, 2)
p is 1
q is 2
"r => 999, s => hello world"
julia>

在定义带有关键字参数的函数时,请记住在关键字/值对之前插入一个分号。

以下是 Julia 手册中的另一个示例。rtol 关键字可以出现在参数列表中的任何位置,也可以省略

julia> isapprox(3.0, 3.01, rtol=0.1)
true

julia> isapprox(rtol=0.1, 3.0, 3.01)
true

julia> isapprox(3.0, 3.00001)
true

函数定义可以结合所有不同类型的参数。以下是一个包含普通参数、可选参数和关键字参数的示例

function f(a1, opta2=2; key="foo")
   println("normal argument: $a1")
   println("optional argument: $opta2")
   println("keyword argument: $key")
end
f (generic function with 2 methods)
julia> f(1)
normal argument: 1
optional argument: 2
keyword argument: foo

julia> f(key=3, 1)
normal argument: 1
optional argument: 2
keyword argument: 3

julia> f(key=3, 2, 1)
normal argument: 2
optional argument: 1
keyword argument: 3

可变参数数量的函数

[编辑 | 编辑源代码]

函数可以定义为可以接受任意数量的参数

function fvar(args...)
    println("you supplied $(length(args)) arguments")
    for arg in args
       println(" argument ", arg)
    end
end

三个点表示著名的splat。这里表示“任意”,包括“无”。您可以使用任意数量的参数调用此函数

julia> fvar()
you supplied 0 arguments

julia> fvar(64)
you supplied 1 arguments
argument 64

julia> fvar(64,65)
you supplied 2 arguments
argument 64
argument 65

julia> fvar(64,65,66)
you supplied 3 arguments
argument 64
argument 65
argument 66

等等。

以下是一个例子。假设您定义了一个接受两个参数的函数

function test(x, y)
   println("x $x y $y")
end

您可以按照通常的方式调用它

julia> test(12, 34)
x 12 y 34

如果您有两个数字,但它们在一个元组中,那么如何将一个数字元组提供给这个需要两个参数的函数呢?答案仍然是使用省略号(splat)。

julia> test((12, 34) ...)
x 12 y 34

省略号或“splat”的使用也称为“拆分”参数

julia> test([3,4]...)
x 3 y 4

您也可以这样做

julia> map(test, [3, 4]...)
x 3 y 4

局部变量和改变参数的值

[编辑 | 编辑源代码]

您在函数内部定义的任何变量在函数结束时都会被遗忘。

function test(a,b,c)
    subtotal = a + b + c
end
julia> test(1,2,3)
6
julia> subtotal
LoadError: UndefVarError: subtotal not defined

如果您想跨函数调用保留值,那么您可以考虑使用全局变量

函数无法修改作为参数传递给它的现有变量,但它可以更改传递给它的容器的内容。例如,以下函数将其参数更改为 5

function set_to_5(x)
    x = 5
end
julia> x = 3
3

julia> set_to_5(x)
5

julia> x
3

虽然函数内部的 x 被更改了,但函数外部的 x 没有被更改。函数中的变量名称对该函数是局部的。

但函数可以修改容器的内容,例如数组。此函数使用 [:] 语法访问容器 x内容,而不是更改变量 x 的值

function fill_with_5(x)
    x[:] .= 5
end
julia> x = collect(1:10);

julia> fill_with_5(x)
5

julia> x
10-element Array{Int64,1}:
5
5
5
5
5
5
5
5
5
5

您可以更改数组的元素,但您无法更改变量,使其指向另一个数组。换句话说,您的函数不允许更改参数的绑定

匿名函数

[编辑 | 编辑源代码]

有时您不想为函数想出一个很酷的名称。匿名函数(无名函数)可以在 Julia 中的许多地方使用,例如与 map() 一起使用,以及在列表推导中。

语法使用 ->,如下所示

x -> x^2 + 2x - 1

这定义了一个无名函数,它接受一个参数,将其称为 x,并返回 x^2 + 2x - 1

例如,map() 函数的第一个参数是一个函数,您可以定义一个一次性函数,该函数仅存在于一个特定的 map() 操作中

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

map() 结束后,函数和参数 x 都消失了

julia> x
ERROR: x not defined

如果您想要一个接受多个参数的匿名函数,请将参数作为元组提供

julia> map((x,y,z) -> x + y + z, [1,2,3], [4, 5, 6], [7, 8, 9])
3-element Array{Int64,1}:
 12
 15
 18

请注意,结果为 12、15、18,而不是 6、15 和 24。匿名函数将每个数组的第一个值加在一起,然后是第二个值,然后是第三个值。

此外,如果您使用“空”元组 (),匿名函数可以具有零个参数

julia> random = () -> rand(0:10)
#3 (generic function with 1 method)

julia> random()
3
julia> random()
1

如果您已经有一个函数和一个数组,您可以通过使用 map() 为数组中的每个元素调用函数。这会依次对每个元素调用函数,收集结果,并将它们返回到一个数组中。这个过程称为映射

julia> a=1:10;

julia> map(sin, a)
10-element Array{Float64,1}:
 0.841471
 0.909297
 0.14112
-0.756802
-0.958924
-0.279415
 0.656987
 0.989358
 0.412118
-0.544021

map() 返回一个新的数组,但如果你调用 map!(),你会修改原始数组的内容。

通常,你不需要使用 map() 来将 sin() 之类的函数应用于数组的每个成员,因为许多函数会自动“逐元素”运算。这两个不同版本的计时是相似的(sin.() 可能占有优势,具体取决于元素的数量)。

julia> @time map(sin, 1:10000);
 0.149156 seconds (568.96 k allocations: 29.084 MiB, 2.01% gc time)
   
julia> @time sin.(1:10000);
 0.074661 seconds (258.76 k allocations: 13.086 MiB, 5.86% gc time)

map() 会将每次应用的结果收集到一个数组中并返回该数组。有时你可能想要“映射”操作,但不想将结果作为数组返回。在这种情况下,请使用 foreach()

julia> foreach(println, 1:20)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

并且 ans 为空(ans == nothingtrue)。

使用多个数组进行映射

[edit | edit source]

你可以使用 map() 来处理多个数组。函数将应用于每个数组的第一个元素,然后是第二个元素,依此类推。数组必须具有相同的长度(与 zip() 函数不同,zip() 函数对长度要求更宽松)。

以下是一个示例,它生成一个英制扳手/套筒尺寸数组。第二个数组只是一堆重复的 32,用于匹配第一个数组中从 5 到 24 的整数。Julia 会为我们简化有理数。

julia> map(//, 5:24, fill(32,20))
20-element Array{Rational{Int64},1}:
 5//32
 3//16
 7//32
 1//4 
 9//32
 5//16
11//32
 3//8 
13//32
 7//16
15//32
 1//2 
17//32
 9//16
19//32
 5//8 
21//32
11//16
23//32
 3//4 

(实际上,一个英制扳手套装不会包含一些奇怪的尺寸——我从未见过一个 17/32 英寸的扳手,但你可以在网上购买。)

使用点语法应用函数

[edit | edit source]

除了 map(),还可以将函数直接应用于作为数组的参数。请参阅关于 向量化函数的点语法 的部分。

归约和折叠

[edit | edit source]

map() 函数收集某个函数对可迭代对象(例如数字数组)的每个元素的运算结果。reduce() 函数执行类似的任务,但在所有元素都被函数处理后,只剩下一个元素。函数应接收两个参数并返回一个参数。数组通过持续应用进行归约,直到只剩下一个元素。

一个简单的示例是使用 reduce() 来对可迭代对象中的数字求和(这与内置函数 sum() 相似)。

julia> reduce(+, 1:10)
55

在内部,它执行类似于以下操作的操作

((((((((1 + 2) + 3) + 4) + 6) + 7) + 8) + 9) + 10)

在每次添加两个数字的操作之后,都会将单个数字传递到下一轮迭代。此过程会将所有数字归约为单个最终结果。

一个更有用的示例是,当你想对可迭代对象中的每个连续对应用函数时。例如,以下是一个函数,它比较两个字符串的长度并返回更长的字符串

julia> l(a, b) = length(a) > length(b) ? a : b
l (generic function with 1 method)

这可以通过逐对处理字符串来找到句子中最长的单词

julia> reduce(l, split("This is a sentence containing some very long strings"))
"containing" 

“This” 持续了几轮,然后被 “sentence” 打败,但最终 “containing” 取得领先,并且之后没有其他挑战者。如果你想看看魔法是如何发生的,请重新定义 l 如下

julia> l(a, b) = (println("comparing \"$a\" and \"$b\""); length(a) > length(b) ? a : b)
l (generic function with 1 method)
 
julia> reduce(l, split("This is a sentence containing some very long strings"))
comparing "This" and "is"
comparing "This" and "a"
comparing "This" and "sentence"
comparing "sentence" and "containing"
comparing "containing" and "some"
comparing "containing" and "very"
comparing "containing" and "long"
comparing "containing" and "strings"
"containing"

你可以使用匿名函数来逐对处理数组。诀窍是让函数留下一个将在下一轮迭代中使用的值。此代码接收一个类似于 [1, 2, 3, 4, 5, 6...] 的数组,并返回 [1 * 2, 2 * 3, 3 * 4, 4 * 5...],将相邻元素相乘。

store = Int[];
reduce((x,y) -> (push!(store, x * y); y), 1:10)
julia> store
9-element Array{Int64,1}:
 2
 6
12
20
30
42
56
72
90

折叠

[edit | edit source]

Julia 还提供了两个相关的函数 foldl()foldr()。它们提供与 reduce() 相同的基本功能。区别在于遍历的方向。在上面的简单求和示例中,我们对 reduce() 操作内部发生的事件的最佳猜测是假设第一对元素首先被添加,然后是第二对元素,依此类推。但是,reduce() 也可能从末尾开始,然后向前面进行。如果这很重要,请使用 foldl() 进行从左到右的遍历,使用 foldr() 进行从右到左的遍历。在许多情况下,结果是相同的,但以下是一个示例,其中你将根据使用哪个版本获得不同的结果

julia> reduce(-, 1:10)
-53
 
julia> foldl(-, 1:10)
-53

julia> foldr(-, 1:10)
-5

Julia 提供了此组中的其他函数:请查看 mapreduce()mapfoldl()mapfoldr()

如果你想对仅接收一个参数的函数使用 reduce()fold-() 函数,请使用一个虚拟的第二个参数

julia> reduce((x, y) -> sqrt(x), 1:4, init=256)
1.4142135623730951

这等效于调用 sqrt() 函数四次

julia> sqrt(sqrt(sqrt(sqrt(256))))
1.4142135623730951

返回函数的函数

[edit | edit source]

你可以像对待其他 Julia 对象一样对待 Julia 函数,特别是在将它们作为其他函数的结果返回时。

例如,让我们创建一个函数创建函数。在此函数内部,将创建一个名为 newfunction 的函数,该函数将把它的参数 (y) 提升到最初作为参数 x 传入的数字。这个新函数将作为 create_exponent_function() 函数的值返回。

function create_exponent_function(x)
    newfunction = function (y) return y^x end
    return newfunction
end

现在我们可以构建许多指数创建函数。首先,让我们构建一个 squarer() 函数

julia> squarer = create_exponent_function(2)
#8 (generic function with 1 method)

以及一个 cuber() 函数

julia> cuber = create_exponent_function(3)
#9 (generic function with 1 method)

趁此机会,让我们构建一个“提升到 4 次幂”的函数(称为 quader,尽管我开始对拉丁语和希腊语的命名感到困惑)

julia> quader = create_exponent_function(4)
#10 (generic function with 1 method)

这些是普通的 Julia 函数

julia> squarer(4)
16
 
julia> cuber(5)
125
 
julia> quader(6)
1296

上面 create_exponent_function() 的定义是完全有效的 Julia 代码,但它不是惯用的。一方面,返回值并不总是需要明确提供——如果未使用 return,则返回最终的评估结果。此外,在这种情况下,函数定义的完整形式可以用更短的一行版本替换。这给出了简洁的版本

function create_exponent_function(x)
   y -> y^x
end

它以相同的方式执行。

make_counter = function()
     so_far = 0
     function()
       so_far += 1
     end
end
julia> a = make_counter();

julia> b = make_counter();

julia> a()
1

julia> a()
2

julia> a()
3

julia> a()
4

julia> b()
1

julia> b()
2

以下是如何创建函数的另一个示例。为了更清楚地看到代码的功能,以下是 make_counter() 函数以略微不同的方式编写

function make_counter()
     so_far = 0
     counter = function()
                 so_far += 1
                 return so_far
               end
     return counter
end
julia> a = make_counter()
#15 (generic function with 1 method)

julia> a()
1

julia> a()
2

julia> a()
3

julia> for i in 1:10
           a()
       end

julia> a()
14

函数链接和组合

[edit | edit source]

Julia 中的函数可以相互结合使用。

函数组合是指将两个或多个函数应用于参数。你可以使用函数组合运算符 () 来组合函数。(你可以在 REPL 中使用 \circ 来输入组合运算符)。例如,sqrt()+ 函数可以像这样组合

julia> (sqrt ∘ +)(3, 5)
2.8284271247461903

它先将数字相加,然后求平方根。

此示例组合了三个函数。

julia> map(first ∘ reverse ∘ uppercase, split("you can compose functions like this"))
6-element Array{Char,1}:
'U'
'N'
'E'
'S'
'E'
'S'

函数链接(有时称为“管道”或“使用管道将数据发送到后续函数”)是指将函数应用于上一个函数的输出

julia> 1:10 |> sum |> sqrt
7.416198487095663

其中 sum() 生成的总数将传递给 sqrt() 函数。等效的组合是

julia> (sqrt ∘ sum)(1:10)
7.416198487095663

管道可以将数据发送到接受单个参数的函数。如果函数需要多个参数,你可能可以使用匿名函数

julia> collect(1:9) |> n -> filter(isodd, n)
5-element Array{Int64,1}:
 1
 3
 5
 7
 9

方法

[edit | edit source]

一个函数可以有许多不同的方法来完成类似的任务。每种方法通常专注于针对特定类型执行任务。

以下是一个函数,用于在输入位置时检查经度

function check_longitude_1(loc)
    if -180 < loc < 180
        println("longitude $loc is a valid longitude")
    else
        println("longitude $loc should be between -180 and 180 degrees")
    end
end
 check_longitude_1 (generic function with 1 method)

如果你在 REPL 中定义此函数,你看到的“通用函数,包含 1 种方法”消息告诉你这时有一种方法可以调用 check_longitude_1() 函数。如果你调用此函数并提供一个数字,它可以正常工作。

julia> check_longitude_1(-182)
longitude -182 should be between -180 and 180 degrees

julia> check_longitude_1(22)
longitude 22 is a valid longitude

但是,如果你输入一个以 Google Maps 上看到的格式显示的经度,会发生什么情况?

julia> check_longitude_1("1°24'54.6\"W")
ERROR: MethodError: `isless` has no method matching isless(::Int64, ::UTF8String)

错误消息告诉我们函数已停止,因为当一个参数是字符串,另一个参数是数字时,小于的概念 (<) 在我们的函数内部没有意义。字符串不小于或大于整数,因为它们是两个不同的东西,所以函数在此处失败。

请注意,虽然check_longitude_1() 函数已经开始执行了。但是参数loc 可以是任何类型 - 字符串,浮点数,整数,符号,甚至是数组。这个函数有许多可能出错的方式。这不是编写代码的最佳方式!

为了解决这个问题,我们可能会想添加代码来测试传入的值,以便以不同的方式处理字符串。但 Julia 提出了一个更好的选择:**方法** 和 多重派发。

在经度以数值形式提供的情况下,loc 参数被定义为“类型为 Real”。让我们重新开始,定义一个新函数,并正确地进行。

function check_longitude(loc::Real)
    if -180 < loc < 180
        println("longitude $loc is a valid longitude")
    else
        println("longitude $loc should be between -180 and 180 degrees")
    end
end

现在这个 check_longitude 函数甚至不会在 loc 中的值不是实数时运行。避免了当值为字符串时该怎么办的问题。使用类型 Real,可以使用任何参数调用此特定方法,前提是它是某种数字。

我们可以使用 applicable() 函数来测试这一点。applicable() 让你知道你是否可以将一个函数应用于一个参数 - 也就是说,对于具有该类型的参数,该函数是否有可用的方法。

julia> applicable(check_longitude, -30)
true 

julia> applicable(check_longitude, pi)
true

julia> applicable(check_longitude, 22/7)
true

julia> applicable(check_longitude, 22//7)
true

julia> applicable(check_longitude, "1°24'54.6\"W")
false

false 表示你不能将字符串值传递给 check_longitude() 函数,因为没有用于该函数的方法可以接受字符串。

julia> check_longitude("1°24'54.6\"W")
ERROR: MethodError: `check_longitude` has no method matching check_longitude(::UTF8String)

现在函数主体甚至不会被查看 - Julia 不知道如何使用字符串参数调用 check_longitude() 函数的方法。

接下来的明显步骤是为 check_longitude() 函数添加另一种方法,只是这一次接受一个字符串参数。这样,一个函数可以被赋予多个备选方法:一个用于数值参数,一个用于字符串参数,等等。Julia 根据你提供给函数的参数类型来选择并运行其中一个可用方法。

这就是**多重派发**。

function check_longitude(loc::String)
  # not real code, obviously!
    if endswith(loc, "W")
       println("longitude $loc is West of Greenwich")
    else
       println("longitude $loc is East of Greenwich")
    end
end
check_longitude (generic function with 2 methods)

现在 check_longitude() 函数有两个方法。要运行的代码取决于你提供给函数的参数类型。你可以避免在函数开始时测试参数类型,因为只有在 loc 是字符串时,Julia 才会将流程分派到字符串处理方法。

你可以使用内置的 methods() 函数来找出你为特定函数定义了多少方法。

julia> methods(check_longitude)
# 2 methods for generic function "check_longitude":
check_longitude(loc::Real) at none:2
check_longitude(loc::String) at none:3

一个有启发性的例子是查看 + 函数有多少种不同的方法。

julia> methods(+)
# 176 methods for generic function "+":
[1] +(x::Bool, z::Complex{Bool}) in Base at complex.jl:276
[2] +(x::Bool, y::Bool) in Base at bool.jl:104
...
[174] +(J::LinearAlgebra.UniformScaling, B::BitArray{2}) in LinearAlgebra at  /Users/osx/buildbot/slave/package_osx64/build/usr/share/julia/stdlib/v0.7/LinearAlgebra/src/uniformscaling.jl:90
[175] +(J::LinearAlgebra.UniformScaling, A::AbstractArray{T,2} where T) in LinearAlgebra at /Users/osx/buildbot/slave/package_osx64/build/usr/share/julia/stdlib/v0.7/LinearAlgebra/src/uniformscaling.jl:91
[176] +(a, b, c, xs...) in Base at operators.jl:466

这是一个很长的列表,列出了当前为 + 函数定义的每种方法;你可以将许多不同类型的对象加在一起,包括数组、矩阵和日期。如果你设计自己的类型,你可能想编写一个将两个类型加在一起的函数。

Julia 选择“最具体的那个方法”来处理参数类型。对于 check_longitude(),我们有两个特定方法,但我们可以定义一个更通用的方法。

function check_longitude(loc::Any)
    println("longitude $loc should be a string or a number")
end
check_longitude (generic function with 3 methods)

loc 参数既不是 Real 数字也不是字符串时,就会调用这种 check_longitude() 方法。它是最通用的方法,如果存在更具体的方法,则根本不会被调用。

方法定义中的类型参数

[编辑 | 编辑源代码]

在方法定义中使用类型信息是可能的。这是一个简单的例子。

julia>function test(a::T) where T <: Real
    println("$a is a $T")
end
test (generic function with 1 methods)
julia> test(2.3)
2.3 is a Float64

julia> test(2)
2 is a Int64

julia> test(.02)
0.02 is a Float64

julia> test(pi)
π = 3.1415926535897... is a Irrational{:π}
julia> test(22//7)
22//7 is a Rational{Int64}
julia> test(0xff)
255 is a UInt8

test() 方法会自动提取传递给它的单个参数 a 的类型,并将其存储在“变量”T 中。对于此函数,T 的定义是**其中 T 是 Real 的子类型**,所以 T 的类型必须是 Real 类型的子类型(它可以是任何实数,但不能是复数)。“T”可以像任何其他变量一样使用 - 在此方法中,它只是使用字符串插值打印出来。(它不一定是 T,但几乎总是这样!)

当你想要将特定方法定义的参数限制为特定类型时,此机制很有用。例如,参数 a 的类型必须属于 Real 数字超类型,因此当 a 不是数字时,此 test() 方法不适用,因为此时参数的类型不是 Real 的子类型。

julia> test("str")
ERROR: MethodError: no method matching test(::ASCIIString)

julia> test(1:3)
ERROR: MethodError: no method matching test(::UnitRange{Int64})

这是一个例子,你可能想要编写一个适用于所有一维整数数组的方法定义。它查找数组中的所有奇数。

function findodds(a::Array{T,1}) where T <: Integer
              filter(isodd, a)
           end
findodds (generic function with 1 method)
julia> findodds(collect(1:20))
10-element Array{Int64,1}:
 1
 3
 5
 7
 9
11
13
15
17
19

但不能用于实数数组。

julia> findodds([1, 2, 3, 4, 5, 6, 7, 8, 9, 10.0])
ERROR: MethodError: no method matching findodds(::Array{Float64,1})
Closest candidates are:
  findodds(::Array{T<:Integer,1}) where T<:Integer at REPL[13]:2

请注意,在这个简单的例子中,因为你没有在方法定义中使用类型信息,你最好坚持使用更简单的方法定义方式,通过在参数中添加类型信息。

function findodds(a::Array{Int64,1})
   findall(isodd, a)
end

但是,如果你想在方法中做一些取决于参数类型的事情,那么类型参数方法就会很有用。

华夏公益教科书