我正在学习Julia语言,我想知道哪个实现可能有更好的性能。
在回答如何检查字符串是否为数字Julia问题时,最后的答案是
isintstring(str) = all(isdigit(c) for c in str)这段代码运行良好,但可以重写。
isintstring = str -> mapreduce(isnumeric, &, collect(str))重写更好还是更糟?还是只是不一样?朱莉娅风格指南似乎没有提供指导。
编辑:正如最初所表达的,这个问题是因为征求意见而不是事实而被屏蔽的。我已重新措辞,以感谢三个突出和有益的答案,原问题。
发布于 2021-12-10 12:40:04
如果您正在使用collect,那么您的代码可能有问题,特别是当它是一种缩减时。因此,您的第二个方法没有必要地分配,而且,它不会提前退出,所以它将一直到字符串的末尾,即使第一个字符不通过测试。
如果您对性能进行基准测试,您还会发现mapreduce(isnumeric, &, collect(str))的速度要慢一个数量级,而且没有考虑早期的救助。
一般情况下:不要使用collect(!),如果可以的话,尽早退出。
本例中的惯用解决方案是
all(isdigit, str)编辑:这里有一些基准测试:
jl> using BenchmarkTools, Random
jl> str1 = randstring('0':'9', 100)
"7588022864395669501639871395935665186290847293742941917566300220720077480508740079115268449616551087"
jl> str2 = randstring('a':'z', 100)
"xnmiemobkalwiiamiiynzxxosqoavwgqbnxhzaazouzbfgfbiodsmhxonwkeyhxmysyfojpdjtepbzqngmfarhqzasppdmvatjsz"
jl> @btime mapreduce(isnumeric, &, collect($str1))
702.797 ns (1 allocation: 496 bytes)
true
jl> @btime all(isdigit, $str1)
82.035 ns (0 allocations: 0 bytes)
true
jl> @btime mapreduce(isnumeric, &, collect($str2))
702.222 ns (1 allocation: 496 bytes) # processes the whole string
false
jl> @btime all(isdigit, $str2)
3.500 ns (0 allocations: 0 bytes) # bails out early
false重写肯定更糟。速度慢,不那么优雅,更冗长。
另一个编辑:我只注意到您现在使用的是isnumeric和mapreduce,而isdigit使用的是all。isnumeric比isdigit更通用,速度更慢,所以这也带来了很大的不同。如果您使用isdigit,并删除collect,则数值字符串的速度差异并不大,但是对于非数字字符串,它仍然没有提前退出,所以最好的解决方案仍然是all(isdigit, str)。
发布于 2021-12-10 08:53:09
您的部分问题是关于命名vs匿名函数的。在第一种情况下,您通过其第一个方法创建了一个函数,并将其分配给隐式const变量isintstring。函数对象本身也会获得名称isintstring,正如您在其类型中看到的那样。不能重新分配变量isintstring
julia> isintstring(str) = all(isdigit(c) for c in str)
isintstring (generic function with 1 method)
julia> typeof(isintstring)
typeof(isintstring)
julia> isintstring = str -> mapreduce(isnumeric, &, collect(str))
ERROR: invalid redefinition of constant isintstring
julia> isintstring = 1
ERROR: invalid redefinition of constant isintstring现在,让我们重新启动REPL,并切换第二种情况下开始的顺序。第二种情况是创建一个匿名函数,然后将其分配给变量isintstring。匿名函数获得一个不能是变量的生成名称。只要不试图声明isintstring (包括方法定义),就可以重新分配const。
julia> isintstring = str -> mapreduce(isnumeric, &, collect(str))
#5 (generic function with 1 method)
julia> typeof(isintstring)
var"#5#6"
julia> isintstring(str) = all(isdigit(c) for c in str)
ERROR: cannot define function isintstring; it already has a value
julia> isintstring = 1
1向命名函数添加方法要容易得多,您需要做的就是使用const名称定义另一个方法,比如isintstring(int, str) = blahblah()。
实际上,向匿名函数添加方法是可能的,但您必须这样做:(::typeof(isintstring))(int, str) = blahblah()。变量isintstring可能并不总是存在,并且匿名函数可以有其他引用,例如func_array[3],在这种情况下,您必须编写(::typeof(func_array[3]))(int, str) = blahblah()。我想你会同意const的名字要清楚得多。
匿名函数倾向于编写为方法调用(如filter(x -> x%3==0, A) )中的参数,其中匿名函数只需要一个方法。在这种情况下,创建一个const-named函数只会使函数命名空间膨胀,并迫使读者跳过代码。实际上,do-blocks的存在是为了允许人们编写一个多行匿名函数,作为第一个参数,而不增加方法调用。
发布于 2021-12-10 14:49:49
就像甘地说“我的生活就是我的信息”一样,朱莉娅说“我的代码就是我的指南”。Julia使使用@less、@edit、methods等工具检查和探索标准和外部库代码变得非常容易。语义风格指南很难确定下来(与语法风格的指南不同),在文档数量和重点方面,Python是一个例外。然而,阅读现有的广泛使用的代码是一个很好的方式来获得什么是共同的风格。
此外,朱利安语篇是比搜索引擎似乎认为它更有用的资源。
现在,对于标题中的问题,“使用功能成语”是一个宽泛而模糊的描述--朱利安风格通常不太强调避免变异(除了性能方面的原因),例如,副作用并不罕见和难闻。高阶函数非常常见,尽管显式map/reduce只是工具库的一部分,包括广播、生成器、理解、隐式为您执行映射的函数(sum(f, A::AbstractArray; dims):“在给定维度上对数组的每个元素调用函数f的结果之和”)等等。
还有另一个需要考虑的因素:性能优于(几乎)任何其他因素。正如其他答案所暗示的那样,您选择哪种风格可能是优化性能的问题。从读取函数开始的代码可以让部分代码开始变异其输入,部分代码成为循环代码,等等,在性能需要的时候。因此,在同一个包中,甚至在同一个文件中,都会看到这些不同样式的混合体。
https://stackoverflow.com/questions/70301156
复制相似问题