怎么(优雅的)拿 Julia 重写带全局变量的代码?


#1

全局变量的值和类型随时都会发生变化。 这使编译器难以优化使用全局变量的代码。 变量应该是局部的,或者尽可能作为参数传递给函数。

Julia 推荐多写函数,但当最里面/最底层的函数需要一个参数时,所有用到这个函数的函数就都得加上这个参数。

如果是常量还好,const 一下当作全局变量影响也不大。如果是个需要经常更新的变量该怎么搞呢?而且这个变量要从头用到尾。是不是只能改算法了。

我现在这样写:cycle_var = f(a, b, c, cycle_var)。然后加上最上面一条,最后代码里全是这样的语句。


举个栗子:

请勿吐槽例子本身 (不要偏题了

  • “怎么不先编译然后 ccall ?” —— 这不是懒嘛,支持全平台的二进制依赖多麻烦,我还想随手去 github 上骗 star。没依赖,直接 copy 就能用多爽。
    既然要编译,不如上 libpng 一把梭。
  • “julia 不擅长 I/O;不是用来干这个的” —— 新到的锤子:hammer:,看啥都是钉子。JuliaOS 我看迟早的事
  • “crc 部分怎么不用 base/crc 库里的” —— 会有的。这不是刚能跑通就来吐槽了么

原项目

  1. svpng/svpng.inc at master · miloyip/svpng
    这个项目就这一个 c 文件。
  2. 极简的 PNG 编码函数 svpng() - 知乎
    这个项目是用来干嘛的

强行改写完

点进去就能看见满屏的 c = f(io, ..., c), 就很难受。

目前我想到的能部分缓解的办法:

  • 学原项目,用宏定义/元编程,自动把要用的全局变量加到函数的参数里
  • 将函数继续拆分,把算 crc 的部分分出来。但只能缓解部分,参数 io 还是没辙。
  • 弃疗,全局变量好。(x)

#2

全局变量写成带有副作用的函数,例如

function foo()
  push!(io, blabla)
end

改写为

function foo(io::IO)
 push!(io, blabla)
end

然后在某个入口提供这个变量。所有全局变量应该都可以这么改写。


#3

对啊,我现在就是这么改的,然后因为最底层的那个函数用了 write(io, xxx) 之后所有的函数就都带 io 这个参数了,调用时也得写成 f(io, xxx)

你去看强行改写完的 svPNG.jlctrl+F 搜一下 io, 再搜一下 c (看函数的参数与返回值) 真的是满屏都是,感觉全局变量从污染代码变成污染眼睛了。

主要是中间那些函数带 io 这个参数就只是为了把它传下去,感觉我多打了很多字。不是很习惯,应该还能抢救一下。


#4

那些 function-like macro 一般就直接改写成closure就行。

function svpng(fp, unsigned w ...)
     SVPNG_PUT(u) = fputc(u, fp)
end

不喜欢nested function也可以用functor.

它这种 header-only 的库,用 BinaryBuilder.jl 10分钟(在提前下好shards的情况下)就能完成7个平台,36个编译器版本的支持。


#5

忘了还有闭包可以用

closure + nested function 看起来还不错


#6

Hi :wave:

I am new here. I lived in HK for 7 years and now in Manila :blush: Hope I can contribute and learn :pray:

Another way to use global variables is to make it a reference like:

const param = Ref{Float64}()

Then you can reference this in your functions like:

function f(x::Float64)
    global param[] += x
end

The thing to remember is you need to use the variable with bracks [] to reference it.

Hope this helps.