关于几个Julia的新手 Mathematica重度玩家问题

各位大佬好,Julia 新手(Mathematica重度玩家)+(刚看了几个小时的Julia 官方Documentation)想请教几个问题:

  1. Julia这种编程语言算是什么范式的呢?比如我之前喜欢用Mathematica,就是典型的泛函式编程。但是我看Julia就觉得不是很美,体现在有的地方可以泛函式,比如可以f\circle g(x) 把函数的返回值作为下个函数的入口。有的地方就有很过程式,比如Control Flow那章。所以作为一个入门者就觉得有点乱,不是很统一,有点就像Mathematica用上了Matlab的函数名加上C/Python的代码风格。

  2. 作为新手不知道怎么查帮助文件。。 求指教。。 我之前就是直接在command line 里面输入? 但是并不能每次都找到相应的函数(比如我根本就不知道函数名,抑或猜一下,抑或搜一个相似的,然后找这个相似的其他refer的函数以企图找到的我想要的)。我不知道有没有什么函数像shell script 一样可以 man -k [keyword] 查找关键词。 我之后就google搜一下我要实现的功能,但是还是不能找到相应的包。 举个例子:我之前一直在找sparse matrix eigenvalue之类的函数,我先是搜了下eigs(因为matlab叫这个名字),然后没有。之后在冗长的Documentation的Sparse Matrix Operation 翻了打半天也没找到。后来下了个IDE叫Julia Pro,然后那个里面貌似是直接Import了大部分的常用库,因此找到了eigs函数。但是还是想知道正常的找函数,因为怎么有效使用帮助文件。

  3. 作为一个MMA的重度玩家,我觉得代码风格不自由相比于MMA。(当然也有可能是我姿势水平太低) 比如

    a. 函数调用为什么不能写中缀表达式和后缀表达式,一定要写成f(x).

    b. 匿名函数为什么不支持变量名也可以匿名?E.g. #->#^2

    c. for 循环有没有可以泛函式的调用?比如我是否可以如此赋值

    a=  for i=[1,2,3]  
                i * i
        end;
    

    然后a就自动赋值为[1,4,9], (我知道map可以一定程度的实现这种,但是原理上还是不一样的,而且map中间匿名函数写一大串好像也不美观) 而不用先定义一个空数组a, 然后a[i]=i*i. 因为相应的MMA有Table函数可以实现这种赋值a=Table[i^2,{i,3}].

d. 写到上面一点,我就想到有没有什么1:n的语法,可以生成数组[1,2,3,…n].

e.突然又想到一点,

[1,2,3] 

[1 2 3]

有什么区别呢? 前者一个返回的是3-element Array{Int64,1}: 后者返回了1×3 Array{Int64,2}: 在MMA里面都是作为List,所以没有区别。 但是在Julia这么区分有什么区别呢?

  1. 最后一个问题可能跟Julia没什么关系。我发现我的Julia 的command line 不能显示所有的Unicode字符。比如\xor就没法显示,只是 一个问号在一个框里。

我问题中对比了很多MMA,并不是想厚此薄彼啊,MMA的效率还是诟病很久,不然我也不会想试试Julia,只是我的以前编程习惯让我想类比更快的学习Julia。

谢谢各位大佬的时间。欢迎讨论!

2 个赞

都是挺好的问题,我先捡几个简单的回答下。
坐等大佬们…

代码风格上,确实有些杂,感觉主要是最初为了吸引不同语言的用户。我记得在 七周七语言 那本书里有提到,说是如果回到最初重新设计Julia语言的话,可能不会这么做了(大意如此)。不过,读核心库以及一些常见库的代码,感觉还是FP更重一些

这确实是个问题,我自己一般按以下顺序:

  1. REPL中,先(搜索)猜,因为用的是字符匹配,这时候尽量用简短点的字符,比如我想找一个函数查看文件是否存在,就会试下exist,file,is等,或者,比如我知道有dir这个函数,因为REPL中?会有函数推荐功能,可以先试下这个相关的函数,看有没有推荐的。(当然,这么做效率很低,我只在某个函数忘了的时候才这么做)
  2. https://docs.julialang.org/en/stable/ 上搜,这时候返回结果一般比较多,如果你知道大概所属的库的话,可以很容易过滤出你想要的部分,然后进去再Ctrl+F
  3. 实在没办法了,我会选择去Slack上提问。。。(把握好提问时机,一般都会有人秒回你)

其实我觉得Clojure的文档非常好 https://clojuredocs.org/ ,社区驱动的,然而Julia并没有类似的东西,或者,咱么可以做一个?

这个可以@冰冰回答下(其实我也很怀恋lisp的风格)

嗯,clojure中就是你说的这种方式,然而Julia中并没有,but,因为有宏,可以做些类似的,参考下Lazy.jl

你是说list comprehension?

 a = [i* i for i=[1,2,3]  ]

看下collect函数

这个应该是你的terminal编码显式的问题(跟你使用的字体也有关系)

Mathematica还有人用。。。。:laughing:

许多新出的语言都有带functional programming的混合风格,比如F#之类的,老语言也在逐渐加入这一style,比如C++和Fortran。

这几年的functional programming的复兴有其深层原因,但不意味着能取代主流。比如在金融领域的jane street是ocaml的最重要金主,但是在其他金融机构主要还是用c++和matlab/python/r.

Numerical computing从Fortran开始到matlab (octave)再到Julia风格都比较稳定,只是加入了更general的其他语言的特色。

  1. 没有明显的范式,可能是因为设计的时候很greedy,从不同的范式中都借鉴了一些东西,想满足不同用户的需求。

  2. 查文档函数最快的方法还是问其它用过的人,自己检索的话,可以使用apropos搜索docstrings里的关键字,能滤出一些候选的函数:

    julia> apropos(“eigenvalue”)
    Base.LinAlg.schur
    Base.LinAlg.schurfact
    Base.LinAlg.eig
    Base.LinAlg.sylvester
    Base.LinAlg.eigvals!
    Base.LinAlg.sqrtm
    Base.LinAlg.eigmin
    Base.LinAlg.lyap
    Base.LinAlg.eigfact
    Base.LinAlg.eigvecs
    Base.LinAlg.eigs
    Base.LinAlg.svds

3.b 匿名函数为什么不支持变量名也可以匿名?E.g. #->#^2
没有用过Mathematica,不太清楚匿名变量名相比于普通的x->x^2再功能上有什么区别,是为了统一风格么?

julia> ♯ -> ♯^2
(::#3) (generic function with 1 method)

3.d 不想用collect的话,也可以加分号[1:n;]

3.c map这类函数都支持do-block的写法:

a = map(1:9) do x 
         k = iseven(x) ? x÷2 : 3x+1
         k^2
    end

最近SO上也有人问类似的问题:

中缀表达式和后缀表达式可以通过宏来实现。然后你也可以用函数管道 |>

x |> f # <=> f(x)

可能是系统原因(Julia在Windows上好像用的不是Powershell,有待考证),我的是好的

julia> ⊻

谢谢!
针对匿名函数,我不知道为什么我输入了

map(#->#^2,[1 2 3])

他就提示我换行了,然后我再按回车就是

ERROR: syntax: incomplete: premature end of input

确实这样,Windows下Julia默认是cmd,但是就算是Powershell也不能显示所有Unicode就很奇怪了。我已经尝试了chcp 65001也不能显示。
不过后来发现了JuliaPro,就直接在Julia Pro上code了

把#换成正常的变量,这里lambda函数的输入需要是合法的变量名。

在windows上我一般用的VS Code,没有出现过不能显示的这个问题。

#在Julia中是注释用的保留关键字,是\sharp 可以用作合法变量名,不过这个不是你说的变量名匿名(我不理解的地方是变量名本来就是可以随意起的,为什么要规定一个特别的“匿名”?Julia里倒是有用_, _, w = f(x,y,z)这种_作为匿名的左值来方便接受不需要的函数返回值)

从其它语言转过来的话,确实可能有类似疑惑。。。

比如,在Clojure里,lambda函数直接用#(* % %)表示,这里%就是它所谓的匿名函数的变量,这么写确实会方便很多,不过Julia中确实没有。。。

1 个赞

明白了,不用写->了,(x,y)->x+y 直接可以写成♯(+, x, y)

我是觉得匿名函数变量名不能匿名的冗余的地方是:(抱歉我又要提到MMA) 在MMA里,匿名函数就可以直接写成#^2&,因此省了一部分"x->". 当然MMA essentially 也会把#^2& parse成 Function[x,x^2]相当于有一个简写的形式。

@Jake +1,这种语法糖确实很方便。Julia里可能要自己写macro来做一些语法糖了,在简单的情况下比如这个:

julia> function _extract_args(ex::Expr)
          x = Symbol[]
          Meta.isexpr(ex, :call) || return x
          for i = 2:length(ex.args)
             arg = ex.args[i]
             arg isa Symbol && push!(x, arg)
             arg isa Expr && append!(x, _extract_args(arg))
          end
          x
       end
_extract_args (generic function with 1 method)

julia> macro ♯(ex)
         Meta.isexpr(ex, :call) || error()
         Expr(:->, Expr(:tuple, _extract_args(ex)...), ex)
       end
@♯ (macro with 1 method)

julia> @macroexpand @♯ x*y+z
:((#18#x, #19#y, #20#z)->#18#x * #19#y + #20#z)

julia> @♯ x*y+z
(::#59) (generic function with 1 method)

julia> (@♯ x*y+z)(1,2,3)
5

julia> @♯ x^2
(::#13) (generic function with 1 method)

julia> (@♯ x^2)(2)
4

julia> (@♯ x+y)(1, 2)
3

julia> (@♯ x+y+z)(1, 2, 3)
6

2 个赞

666

挺有意思的一个实现,不过,这里_extract_args会把所有的Symbol都替换成参数(话说用MacroTools实现这个还挺方便的),一般不会这么写(因为会覆盖默认context中的变量),在Clojure中是通过%,%1,%2(抱歉并不了解MMA…)指定参数的。

额。。我还是不太理解用%,%1,%2x,x1,x2的区别,是因为scope问题嘛?然后为了解决问题%在Clojure里是保留的关键字?

其实就是不用预先定义#1,#2,#3啦

:dizzy_face: :dizzy_face: :dizzy_face: 这下更糊涂了,像我上面写的那个例子(@♯ x*y+z)(1,2,3), 这里(1,2,3)是直接作为字面量传进去的,并不需要预先定义x, y, z

不用太在意这些细节,呵呵,anyway,用macro反正是完全可以实现的

举个例子:

a = 3
reduce((x,y) -> (x+y+a), 1:10)
reduce(@# %1 + %2 + a, 1:10)
reduce(@# x + y + a, 1:10)

可以体会下这三者的区别

1 个赞