Julia: Structuring code with many different but related algorithm choices

前端 未结 3 1605
旧巷少年郎
旧巷少年郎 2021-01-20 11:24

I am looking for an elegant way to re-arrange my code. For developing solvers, what happens is you can have a lot of different options which have the same setup. For example

相关标签:
3条回答
  • 2021-01-20 11:54

    I am not sure if this is a better approach than using a state object, but you can use macros to achieve what you want:

    macro f() 
        quote 
            b = 5
            x = a * b
            x  # the value of a block is equal to its last statement
        end
    end
    
    function foo()
        a = 2
        b = 3
        x = @f()
        x, b  # (10,3)
    end
    

    Note that Julia automatically replaces b and x within the macro with a unique name to avoid side effects. If you want to have side effects, you can use the following:

    macro g() 
        esc(quote 
            b = 5
            x = a * b
        end)
    end
    
    function bar()
        a = 2
        b = 3
        @g()
        x, b  # (10,5)
    end
    

    This is equivalent to replacing @g() with the code between quote and end from the definition of g. One can also define a small convenience macro:

    macro def(name, definition)
        return quote 
            macro $(esc(name))()
                esc($(Expr(:quote, definition)))
            end
        end
    end
    

    With that, g could have been defined as

    @def g begin
        b = 5
        x = a*b
    end
    
    0 讨论(0)
  • 2021-01-20 12:18

    The way to do this is just to pass the solver function as a parameter to the solve function:

    solver1(state) = "Solver 1 with state $state"
    
    function solve(solver)
    
        # set up the state here, e.g. in a State object
    
        state = [1, 2]
    
        result = solver(state)
    
    end
    
    solve(solver1)
    

    "Accessing the same scope" is the same as passing over a variable containing the local state you need. "Having effects" is the same as passing back variables from the solver method.

    If the solver functions are sufficiently simple, they will be inlined by the compiler into the solve function, and it will be as if you had typed them in directly (if you were worried about the overhead of the function call).

    EDIT: Didn't read carefully enough. The "long trail of parameters" you mention you can just store into a special type, e.g.

    type SolverParams
        a::Int
        b::Float64
        params::Vector{Float64}
    end
    

    Then each solver takes an argument of this type. Or it could just be a tuple that you pass into the solver.

    0 讨论(0)
  • 2021-01-20 12:19

    Since julia functions can modify their arguments, side effects can usually be handled by making whatever you want to modify one of the arguments of the function.

    This demo uses anonymous functions to allow your solvers to take different parameters, if you want. I'm not sure it's exactly what you're asking, but if you don't know about this already it might be informative.

    using Base.Test
    
    function solver1(data, initialize::Bool)
        if initialize
            fill!(data, 0)
        end
        return 1
    end
    function solver2(data, hellostring)
        println(hellostring)
        return 2
    end
    
    function solver(f)
        data = [1,2,3,4]
        ret = f(data)
        println(sum(data))
        return ret
    end
    
    @test solver(data->solver1(data, false)) == 1
    @test solver(data->solver1(data, true)) == 1
    @test solver(data->solver2(data, "hello, world")) == 2
    

    which generates the output

    10
    0
    hello, world
    10
    
    0 讨论(0)
提交回复
热议问题