请问怎么创建一个swap函数


#1

在julia中,对简单的类型比如Int,在函数调用时

比如函数foo(a),让b=2,调用foo(b)是b通过传值赋值给a,a和b的值互不影响

如果有一个swap函数,他只能对struct等复杂类型起作用,因为这时参数按引用传递

假设我定义一个宏

2019-08-27%2022-42-53%20%E7%9A%84%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE

调用它

2019-08-27%2022-43-05%20%E7%9A%84%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE

a和b的值还是没变,可以确定的是在repl中直接写宏中的语句是可以达到目的的

换一种想法

2019-08-27%2022-43-12%20%E7%9A%84%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE

试着这样定义一个宏

2019-08-27%2022-43-21%20%E7%9A%84%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE

调用出错

2019-08-27%2022-43-32%20%E7%9A%84%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE

请问我的宏有没有写错,另外除了a,b=b,a就没有别的方案了吗


#2

多看文档,关于卫生宏的内容

julia> macro swap(a,b)
           :(($a,$b)=($b,$a))|>esc
       end
@swap (macro with 1 method)

julia> (a,b)=(1,2)
(1, 2)

julia> @swap a b
(2, 1)

julia> (a,b)
(2, 1)

#3

建议稍微看一下 Markdown Reference

截图还是有所不便。

这样输入代码

```julia
julia> # 代码写在这里

julia> macro swap(a,b)
           temp = a
           a = b
           b = temp
       end
@swap (macro with 1 method)

julia> 
```

显示为

julia> # 代码写在这里

julia> macro swap(a,b)
           temp = a
           a = b
           b = temp
       end
@swap (macro with 1 method)

julia> 

你要修改宏调用所在环境的变量,这个宏就“不卫生”了,需要转义一下,让相关的变量在宏调用所处环境中解析。

macro swap(x, y)
quote
	local temp = $x
	$x = $y
	$y = temp
end |> esc
end

效果

julia> a, b = 1, 2
(1, 2)

julia> a, b
(1, 2)

julia> macro swap(x, y)
       quote
           local temp = $x
           $x = $y
           $y = temp
       end |> esc
       end
@swap (macro with 1 method)

julia> macroexpand(Main, :(@swap(a,b)))
quote
    #= REPL[75]:3 =#
    local temp = a
    #= REPL[75]:4 =#
    a = b
    #= REPL[75]:5 =#
    b = temp
end

julia> @swap(a, b)
1

julia> a,b
(2, 1)

你提到的另一种方法

macro swap2(x, y)
quote
	$x,$y = $y,$x
end |> esc
end

效果

julia> a, b = 1, 2
(1, 2)

julia> macro swap2(x, y)
       quote
           $x,$y = $y,$x
       end |> esc
       end
@swap2 (macro with 1 method)

julia> macroexpand(Main, :(@swap2(a,b)))
quote
    #= REPL[97]:3 =#
    (a, b) = (b, a)
end

julia> @swap2(a, b)
(2, 1)

julia> a, b
(2, 1)

#4

thank you both:hushed:


#5

有些疑问
当初设计宏的时候为什么不直接把宏做成转义


#6

设计如此。宏是在代码parse阶段的AST的函数(输入AST,输出变换后的AST)。为了保证宏的引入不会给你的环境造成奇怪的影响,你在函数里使用的变量,都会被gensym生成一个不和其他变量冲突的名字。如果你就是要影响当前环境,就需要显式esc

要的就是显式,让你知道你自己在做什么。这就和因为Java默认内部类非静态,容易导致内存泄漏,所以Kotlin干脆让你默认静态,想要非静态请手动inner class一样,没啥可问的。


#7

推荐的做法是什么,显式调用esc吗


#8

首先,因为宏是对AST的变换,你拿到的只是一个个符号,没有任何的上下文环境、类型信息等。如果你确实需要根据传入的:a去操作当前名为a的变量,你就esc呗

从一个系统/语言的设计(design)的原则上来,为了降低人犯错的概率,如果有得选,操作代价最小、最顺手的操作应该是最安全的那种。在工业界你能找到很多例子(比如为什么刹车比油门踏板大这么多?)