10 yuan programming

10 yuan programming

Metaprogramming

What is metaprogramming

The explanation on Wikipedia is:

Metaprogramming (English: Metaprogramming), also translated as hyperprogramming, refers to the writing of certain types of computer programs that write or manipulate other programs (or themselves) as their data, or complete parts that should be compiled at runtime Work done at the same time. In most cases, compared with writing all the code manually, programmers can get higher work efficiency, or give the program more flexibility to deal with new situations without recompiling.

Didn’t you understand? It doesn't matter, because I didn't understand it at first. Zhihu has an explanation on metaprogramming that is relatively intuitive.

Know the original answer

The original meaning of the prefix Meta- in Greek is "behind, past...", similar to Latin post-, for example, metaphysics means "after physics". This word originally refers to some Aristotles Works, because they are usually sorted after Physics. But the western philosophical circles have gradually given the affix a whole new meaning in thousands of years: something about something itself. For example, meta-knowledge is "knowledge about knowledge itself", meta-data is "data about data", meta-language is "language about language", and meta-programming is also from this, it is "about programming Programming". After clarifying the etymology and literal meaning, it can be seen that the mainland's translation of the prefix meta- as "yuan" is inappropriate. Taiwan, China, is translated as "meta", which is a little better, but it is still unclear. Maybe "autocorrelation" is a good choice, "autocorrelation data", "autocorrelation language", "autocorrelation programming"-but it seems too wordy. Anyway. First look at the meta-data: "My phone number is +86 123 4567 8910"-this is a piece of data; "+86 123 4567 8910 has thirteen numbers and one character, the first two are the country code, and the next is a Mobile phone number"-this is the data about the previous piece of data.

So what is meta-programming if you take a picture of a cat and draw a tiger? Broadly speaking, as long as it is programming-related programming, it is considered meta-programming-for example, if program A can output A-Z, then writing program A is considered "programming"; and program B can generate program A (maybe Run it together to output A-Z), then the activity of writing program B can be counted as meta-programming, "meta-programming."

Then let's take a look at what exactly is metaprogramming in Julia and how to apply it?

Program representation

Every Julia program starts with a string

str1 = "1 + 1"
>>"1 + 1"
# Parse the string into an expression
ex1 = Meta.parse(str1)
>>:(1 + 1)
typeof(ex1)
>>Expr
# We can also directly define the form of parse
ex2 = :(1 + 1)
typeof(ex2)
>>Expr

The Expr object contains two parts, one is the Symbol that identifies the type of expression, and the other is the parameter of the expression

fieldnames(typeof(ex1))
>>(:head, :args)
ex1.head
>>:call
ex1.args
>>3-element Array{Any,1}:
  :+
 1
 1
Meta.show_sexpr(ex1)
>>(:call, :+, 1, 1)

So we can also use prefix notation to construct

ex3 = Expr(:call, :+, 1, 1)
:(1 + 1)

Of course we can also find the result of the expression

eval(ex3)
>>2

The dump function can display Expr objects

dump(ex1)
>>Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol +
    2: Int64 1
    3: Int64 1

Symbol

Construct a Symbol

:foo
>>:foo
typeof(ans)
>>Symbol
:foo == Symbol("foo")
>>true

If there are multiple parameters in Symbol, it means to connect these parameters

Symbol("foo",123)
>>:foo123

Quote

The expressions we defined earlier are all numeric expressions, and metaprogramming also allows character expressions

ex4 = :(a+b*c+1)
>>:(a + b * c + 1)
typeof(ex4)
>> Expr

If it is a multi-line expression, it can be quote...endimplemented in

ex4 = quote
    x = 1
    y = 2
    x + y
end
typeof(ex4)
>>Expr

You can use the $ symbol to substitute values ​​into symbolic expressions

a = 1
ex = :($a + b)
>>:(1 + b)

If it is an array expression, the following form must be used

args = [:x, :y, :z]
:(f(1, $(args...)))
>>:(f(1,x,y,z))

Nested Quote

x = :(1 + 2)
>>e = quote quote $x end end

At this point we use to evalview the result of this expression

eval(e)
>>quote
    #= none:1 =#
    1 + 2
end

In other words, the content of the expression is still a quote; how do we take out the innermost content?

e = quote quote $$x end end
eval(e)
>>quote
    #= none:1 =#
    3
end

Expression in function

function math_expr(op, op1, op2)
    expr = Expr(:call, op, op1, op2)
    return expr
end
ex = math_expr(:+, 1, Expr(:call, :*, 4, 5))
>>:(1 + 4 * 5)
eval(ex)
>>21
function make_expr2(op, opr1, opr2)
   opr1f, opr2f = map(x -> isa(x, Number)? 2*x: x, (opr1, opr2))
   retexpr = Expr(:call, op, opr1f, opr2f)
   return retexpr
end
make_expr2(:+, 1, 2)
>>:(2 + 4)
ex = make_expr2(:+, 1, Expr(:call, :*, 5, 8))
>>:(2 + 5 * 8)
eval(ex)
>>42

Macros

Macro is also an important application of Julia metaprogramming. Macro is a kind of rule, or grammatical substitution. This substitution is performed during pre-compilation. Macro does not have a function-like call time at runtime.

macro HelloWorld()
    return :( println("Hello World!")
end

The compiler will replace all @HelloWorld with:( println("Hello World!")

@HelloWorld()
>>Hello World!

Macro can also take parameters

macro ILike(str)
    return :( println("I like ", str))
end
@ILike("Julia")
>>I like Julia

We can macroexpandsee the quote expression returned, which is also a very important tool for macro debugging

ex = macroexpand(Main, :(@ILike("Julia")))
>>:((Main.println)("I like ", "Julia"))
typeof(ex)
>>Expr

You can also @macroexpandview the quote expression returned

@macroexpand @ILike "Julia"
>>:((println)("I like ", "Julia"))

@macroexpandThere is another important use, let’s look at the following example

macro testExpend(arg)
    println("I execute at parse time. The argument is: ", arg)
    return :(println("I execute at runtime. The argument is: ", $arg))
end

Two println statements, one is julia statement and one expression, if we run directly@testExpand((1,2))

@testExpand((1,2,3))
>>I execute at parse time. The argument is: (1, 2, 3)
I execute at runtime. The argument is: (1, 2, 3)

Both println statements have output; if we define an expression

ex = macroExpand(Main, :(@testExpand :(1,2,3)))
>>I execute at parse time. The argument is: $(Expr(:quote, :((1, 2, 3))))

It can be seen that when the expression is defined, the content of the first println is printed out

eval(ex)
>>I execute at runtime. The argument is: (1, 2, 3)

The second println function is executed when the expression is run

Macro call

We also called Macro before, for example @ILike("Julia"), but the syntax of macro calling is very particular, let's take a look at it in detail below. Two commonly used macro calling methods

@name expr1 expr2 ...
@name(expr1, expr2, ...)

It should be noted that: in the first usage, the two parameters are separated by a space, and there is no comma between the parameters; in the second usage, there is no space between the name and (), and there is a comma between the parameters. open.

If it is written

@name (expr1, expr2, ...)

It means that the parameter is a tuple.

When calling array, the following methods can be used

@name[ab] * v
@name([ab]) * v

For Macro parameters, we can showprint them out

macro showarg(x)
    show(x)
end
@showarg(a)
>>:a
@showarg(1+1)
>>:(1 + 1)

Each Macro will be passed in two additional parameters: __source__and __module__, they can indicate the location, which is very useful when debugging.

Since Julia is multiple dispatch, Macro also supports multiple methods similar to functions. Let's look at an example of debugging with Macro.

debugging = true

macro debug1(msg...)
    if debugging
        :(println("DEBUG> ", $(msg...)))
    else
        :nothing
    end
end

macro debug1(lineno, msg...)
   if debugging
        :(println("DEBUG ", basename(@__FILE__),":",$lineno, "> ", $(msg...)))
    else
        :nothing
    end
end

function bubble_sort!(xs::Vector)
    println("bubble_sort starting...")
    n = length(xs)
    swapped = true
    while swapped
        swapped = false
        for i in 1:n-1
            if xs[i]> xs[i+1]
                # println(xs)
                xs[i+1], xs[i] = xs[i], xs[i+1]
                @debug1(@__LINE__, join(xs, ""))
                swapped = true
            end
        end
    end
    xs
end

xs = Vector([1, 3, 4, 5, 8, 2, 7])
res = bubble_sort!(xs)

The result of the operation is

bubble_sort starting...
DEBUG bubble_sort.jl:34> 1 3 4 5 2 8 7
DEBUG bubble_sort.jl:34> 1 3 4 5 2 7 8
DEBUG bubble_sort.jl:34> 1 3 4 2 5 7 8
DEBUG bubble_sort.jl:34> 1 3 2 4 5 7 8
DEBUG bubble_sort.jl:34> 1 2 3 4 5 7 8

Commonly used macros

  • @time
  • @show
  • @which

@time

@time 1 + 2
@time println("Hello world!")
@time sleep(1)

@time is achieved by the following method.

macro tid(expre)
    quote
        local t0 = time()
        local val = $expre
        elapsedtime = time()-t0
        println("$elapsedtime seconds")
        val
    end
end

@tid map(x->x^2, 1:10000)

@which

@which 1+2
@which sleep(2)

@show

x = rand(10)
@show sum(x)
@show cumsum(x)
Reference: https://cloud.tencent.com/developer/article/1653103 10 Yuan Programming-Cloud + Community-Tencent Cloud