在Julia中,max()和maximum()之间有什么区别?

3
我目前正在学习Julia中的内置函数,特别是maxmaximum,并且我想要弄清楚它们之间的区别。
julia> max(1, 2, 3, 4)
4
julia> maximum([1, 2, 3, 4])
4

help?> max
search: max maximum maximum! maxintfloat argmax typemax findmax findmax! Cintmax_t floatmax Cuintmax_t Matrix minmax BitMatrix macroexpand @macroexpand

  max(x, y, ...)


  Return the maximum of the arguments (with respect to isless). See also the maximum function to take the maximum element from a collection.

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> max(2, 5, 1)
  5

我看不出这两个函数之间有什么区别。似乎maximum()max()功能更强大,可以完成max()能做的所有事情。
为什么它们要分成两个不同的函数?为什么两者同时存在?你能给我一些例子,说明这两个函数在不同的上下文中起作用吗?

1
你说你看不出区别,然后又描述“最大”具有更多功能。它不能既不同又相同。 - Clifford
1
你说你看不出区别,然后又描述了“最大值”具有更多的功能。它不能既不同又相同。 - undefined
1
也许你意味着你不明白为什么两者都需要存在? - Clifford
3个回答

4
你在帖子中附上的文档清楚地表明了:
“另请参阅最大函数,以从集合中获取最大元素。”
`max()` 函数操作独立参数的列表,而 `maximum()` 函数操作单个参数,该参数是一个“集合”。
在你的简单且不切实际的示例中,当然没有区别,因为你从文字值创建了一个临时实例作为集合。在更现实的应用中,你会根据正在处理的输入使用适合的方法,这可能是一个集合,也可能不是。虽然你可以以同样的方式从独立变量实例化一个临时集合,但这将是一种不必要的处理开销。
在只有文字值的示例中,使用其中之一可能不太清楚。但这是一个不切实际的示例。如果你有多个独立变量,将它们整理成一个集合只是为了确定最大值将是低效的。
x = 0
y = 10
z = 5
max( x, y, z )      # maximum of x, y, z
maximum( [x,y,z] )  # Create a temporary collection from x,y,x and return maximum

a = [x,y,z]
maximum( a )        # Maximum of an existing collection
max( a )            # ERROR: a is not a collection.

互动地访问https://julialang.org/learning/tryjulia/

julia> x=1
1

julia> y=3
3

julia> z=2
2

julia> a=[x,y,z]
3-element Vector{Int64}:
 1
 3
 2

julia> maximum(a)
3

julia> max(a)
ERROR: MethodError: no method matching max(::Vector{Int64})
Closest candidates are:
  max(::Any, ::Missing) at missing.jl:130
  max(::Any, ::Any) at operators.jl:419
  max(::Any, ::Any, ::Any, ::Any...) at operators.jl:560
  ...
Stacktrace:
 [1] top-level scope
   @ REPL[6]:1

julia> max(x,y,z)
3

julia> ^C

julia> 

因此,您使用适合您具有的数据类型的函数,而您具有的数据类型取决于您在更大程序的广泛上下文中所做的工作。如果要迭代处理大量数据或处理在运行时“收集”的可能会随时间添加或删除的数据,则不太可能使用独立变量。


你能提供更实际的应用例子吗?对我来说,找到它们之间的关键区别非常有帮助。 - Pham Hung
你能提供更加实际的应用例子吗?对我来说,找到它们之间的关键区别非常有帮助。 - undefined
说实话,在你的问题之前,我从未听说过Julia,并且也从未见过任何Julia代码。不过,我可以阅读和理解文档,并且熟悉其他几种编程语言以及计算机的工作原理。所以,我可能不是最适合回答这个问题的人。不过,你应该选择适用于你所拥有的数据类型的函数。将多个单独的值整合到一个“集合”中,仅仅为了使用maximum()而不是max()是低效的。当你处理“变量”时,这就成为一个问题。你提供的带有字面值的示例并不是非常现实。 - Clifford

2
正如Clifford已经指出的那样,maximum操作适用于集合,而集合的大小通常在编译时是未知的。话虽如此,也可以使用max来处理集合,方法是使用...,但据我理解,不建议对任何大小在编译时不确定的集合使用该方法。
注意:我不是一个编译器专家,尤其不是Julia编译器专家。所以,我不知道在不同情况下会发生什么。这只是为了强调maxmaximum不能互换使用。
julia> xs = rand(10)
10-element Vector{Float64}:
 0.34747000762279256
 0.00868217104832747
 0.13218902452742898
 0.8467868702183277
 0.27549007470760745
 0.12099609627549013
 0.6724461023554089
 0.6493350852320885
 0.6318376513505265
 0.7677036329934219

julia> maximum(xs)
0.8467868702183277

julia> max(xs...)
0.8467868702183277

maximum在集合上进行循环,显而易见。至少从本地代码的角度来理解max的作用,需要对调用约定(例如参数如何传递给函数)有一些了解。max操作寄存器和通过堆栈传递的值,即它要求在编译时知道项目的数量。maximum则在一些堆分配的数据上操作。

通常情况下,集合的大小在编译时是未知的,因此在一些不在REPL中的通用代码中使用max可能会导致类型不稳定,从而严重降低速度。

julia> xs = rand(1000);

julia> @code_native max(xs...)
... 
    vmovups %ymm0, 7304(%rax)
    vmovups 7312(%rbp), %ymm0  # move value in stack to register ymm0
    vmovups %ymm0, 7336(%rax)  # move from register ymm0 to some other address
    vmovups 7344(%rbp), %ymm0  # repeat for all arguments...
    vmovups %ymm0, 7368(%rax)
    vmovups 7376(%rbp), %ymm0
    vmovups %ymm0, 7400(%rax)
    vmovups 7408(%rbp), %ymm0
    vmovups %ymm0, 7432(%rax)
    vmovups 7440(%rbp), %ymm0
    vmovups %ymm0, 7464(%rax)
    vmovups 7472(%rbp), %ymm0
    vmovups %ymm0, 7496(%rax)
    vmovups 7504(%rbp), %ymm0
    vmovups %ymm0, 7528(%rax)
    vmovups 7536(%rbp), %ymm0
    vmovups %ymm0, 7560(%rax)
    vmovups 7568(%rbp), %ymm0
    vmovups %ymm0, 7592(%rax)
    vmovups 7600(%rbp), %ymm0
    vmovups %ymm0, 7624(%rax)
    vmovups 7632(%rbp), %ymm0
    vmovups %ymm0, 7656(%rax)
    vmovups 7664(%rbp), %ymm0
    vmovups %ymm0, 7688(%rax)
    vmovups 7696(%rbp), %ymm0
    vmovups %ymm0, 7720(%rax)
    vmovups 7728(%rbp), %ymm0
    vmovups %ymm0, 7752(%rax)
...
    vmovups %ymm0, 7912(%rax)
    vmovups 7920(%rbp), %ymm0
    vmovups %ymm0, 7944(%rax)
    movq    %rax, 80(%rsp)
    movabsq $139643313038512, %rcx          # imm = 0x7F013E11A4B0
    movq    %rcx, 96(%rsp)
    movabsq $139643322340352, %rcx          # imm = 0x7F013E9F9400
    movq    %rcx, 104(%rsp)
    movq    %r15, 112(%rsp)
    movq    %rax, 120(%rsp)
    movabsq $jl_f__apply_iterate, %rax
    leaq    96(%rsp), %rsi
    xorl    %edi, %edi
    movl    $4, %edx
    vzeroupper
    callq   *%rax
...

看起来,这就是正在发生的事情——整个数组“被复制到堆栈”(地址偏移自%rbp寄存器,引号表示这在编译时发生,某种程度上),然后再次被复制到内存中的某个位置(指针存储在%rax加上一些偏移量)。考虑以下基准测试。
julia> using BenchmarkTools

julia> @btime max(xs...)
  52.051 μs (2003 allocations: 55.00 KiB)
0.9999736504713679

julia> @btime max($(xs...))
  2.017 ns (0 allocations: 0 bytes)
0.2307205152110382

julia> @btime maximum(xs)
  849.239 ns (1 allocation: 16 bytes)
0.9999736504713679

在一般情况下,由于集合大小在编译时是未知的,所以你将会达到第一个基准时间。
最后,请考虑以下基准测试。
julia> x1, x2, x3, x4, x5 = (1,2,3,4,5)
(1, 2, 3, 4, 5)

julia> xs = [x1,x2,x3,x4,x5];

julia> @btime maximum($(xs))
  7.746 ns (0 allocations: 0 bytes)
5

julia> @btime max($(x1), $(x2), $(x3), $(x4), $(x5))
  3.893 ns (0 allocations: 0 bytes)
5

长话短说:当参数数量较少且在编译时已知时,请使用max;当使用堆分配的集合时,请使用maximum。两者都有各自的用途。

编辑:什么时候可以使用max(xs...)

xs类似于Tuple或者甚至是StaticArray时,因为元素数量在编译时已知。


很遗憾一个实施问题扩大到用户API层面上了... - Antonello
很遗憾,一个实施问题扩大到用户API... - undefined
1
我不认为那是原因,@Antonello。我相信maxmaximum被视为语义上不同的函数。请参见例如https://github.com/JuliaLang/julia/issues/11872 - DNF
1
我不认为那是原因,@Antonello。我认为maxmaximum被视为语义上不同的函数。请参见例如 https://github.com/JuliaLang/julia/issues/11872 - undefined

1

maximum接受一个函数参数:

julia> maximum(length, ["Julion", "Julia", "Jule"])
6

max 使用 isless()。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接