【问题标题】:How to call function from within constructor of a struct in Julia?如何从 Julia 结构的构造函数中调用函数?
【发布时间】:2018-08-19 09:29:00
【问题描述】:

我是一名具有 C++ 和 Python 背景的程序员,最近偶然发现了 Julia,我真的很喜欢它所提供的功能。为了同时更熟悉区块链实现和 Julia,我有点雄心勃勃,正在尝试通过转换 Hackernoon 发布的 Python 实现在 Julia 中创建区块链的基本实现(作者解释了每种方法应该比我做得更好的地方)。

但是,我在创建实际的 Blockchain 结构时遇到了问题。为了创建创世块,Hackernoon 建议我在构造函数中调用成员函数new_block。到目前为止,我还没有弄清楚如何在 Julia 中最好地复制它。到目前为止,这是我所拥有的:

import JSON
import SHA

mutable struct Blockchain
    chain::Array{Dict{String, Any}}
    current_transactions::Array{String}
    block::Dict{String, Any}
    new_block::Function

    function Blockchain(chain::Array{Dict{String, Any}}, current::Array{String})    
        new(chain, current, new_block(previous_hash=1, proof=100)) 
        # ^issue calling function within the constructor here
    end
end

当我尝试运行我的代码时,我收到以下错误:

invalid redefinition of constant Blockchain.

这是new_block 函数:

function new_block(proof::String, previous_hash::String=nothing)
    block = Dict(
    "index" => length(chain) + 1,
    "timestamp" => time(),
    "transactions" => current_transactions,
    "proof" => proof,
    "previous_hash" => previous_hash | hash(chain[end]),
    )
    current_transactions = []
    append!(chain, block)
    return block
end

以下是我目前拥有的其他功能:

function new_transaction(this::Blockchain, sender::String, recipient::String, amount::Int)
    
     transaction = Dict(
        "sender"=> sender,
        "recipient"=> recipient,
        "amount"=> amount,
     )

    append!(this.current_transactions, transaction)

    return length(this.chain) + 1
end

function hash(block::Blockchain)
    block_string = JSON.dumps(block, sort_keys=true).encode()
    return sha256(block_string).hexdigest()
end

我可能对类型/结构在 Julia 中的工作方式有一些误解;我的大部分信息是从第三方网站以及官方文档中获得的。以下是我一直依赖的一些来源:

以更聪明/更有效的方式尝试完成我的工作将不胜感激。

编辑:

以下是我根据给定建议所做的一些更改:

struct Blockchain
    chain::Array{Dict{String, Any}}
    current_transactions::Array{String}

    function Blockchain(chain::Array{Dict{String, Any}}, current::Array{String})    
        new(chain, current)
    end
end

function new_block!(this::Blockchain, proof::Int, previous_hash::Int=nothing)
    block = Dict(
    "index" => length(this.chain) + 1,
    "timestamp" => time(),
    "transactions" => this.current_transactions,
    "proof" => proof,
    )
    if previous_hash == nothing
        block["previous_hash"] = hash(this.chain[end])
    else
        block["previous_hash"] = previous_hash
    end

    this.current_transactions = []
    append!(this.chain, block)
    return this
end

我意识到block 属性没有用,因为它只是为了添加到chain 而存在,所以我将其删除。

此外,这里还有一个没有内部构造函数的替代 Blockchain 定义:

struct Blockchain
    chain::Array{Dict{String, Any}}
    current_transactions::Array{String}
    
    Blockchain(x::Array{Dict{String, Any}}, y::Array{String}) = new(x, y)
end

【问题讨论】:

  • 你好维克多。首先,错误消息invalid redefinition of constant Blockchain 与您的new_block 函数完全无关。只是不能在 Julia 中重新定义类型(结构)。一旦它被定义,你就不能再改变它了。
  • 调用构造函数Blockchain(chain, current) 时真正得到的错误是ERROR: UndefVarError: new_block not defined,这很有意义。我建议根本不要将 new_block 函数放入结构中。通常在 Julia 中,您将“属性”分别放入结构和函数中。
  • 所以你的new_block 应该简单地将Blockchain 对象作为第一个参数。您可以访问和修改此Blockchain 的字段。
  • 这是有道理的。这是否意味着构造函数应该与new_block 一起超出struct
  • @victor-alves,构造函数可以保持在struct定义内。但是,inner constructors 将对您的代码产生另一种影响,,Julia 的编译器不会生成默认构造函数。基本上,您的原始问题与您的构造函数无关,也与new_block 无关。正如错误已经暗示的那样,您无法重新定义类型。然后,当您解决该问题时,您将面临另一个问题,因为您正在尝试调用要创建的对象的一部分(尚未完成)

标签: function methods constructor julia blockchain


【解决方案1】:

免责声明。这可能一定是您问题的答案。但我想将其发布为答案,因为评论不允许让我轻松表达以下内容。

mutable struct Blockchain
    chain::Array{Dict{String, Any}}
    current_transactions::Array{String}
    block::Dict{String, Any}
    new_block::Function

    function Blockchain(chain::Array{Dict{String, Any}}, current::Array{String})    
        new(chain, current, new_block(previous_hash=1, proof=100)) 
        # ^issue calling function within the constructor here
    end
end

在这里,我假设您正在尝试添加一些成员函数功能到您的struct,因为您已经声明您来自C++ 背景。然而,这不是朱利安。在 Julia 中,正如 @crstnbr 已经建议的那样,我们需要定义作用于对象的全局函数。惯例是您在函数末尾添加!,以表明该函数将至少更改其参数之一。

然后,通过检查new_block 的定义:

function new_block(proof::String, previous_hash::String=nothing)
    block = Dict(
    "index" => length(chain) + 1, # which chain?
    "timestamp" => time(),
    "transactions" => current_transactions, # which current_transactions?
    "proof" => proof,
    "previous_hash" => previous_hash | hash(chain[end]), # which chain?
    )
    current_transactions = []
    append!(chain, block) # which chain?
    return block
end

我注意到几个严重的错误。首先,您尝试使用一些未定义的变量,例如chaincurrent_transactions。我假设,再次从C++ 开始,您认为new_block 将是Blockchain 的成员函数,因此可以看到它的chain 成员变量。这不是Julia 的工作原理。第二个问题是你如何尝试调用new_block

new_block(previous_hash=1, proof=100)

这个调用是完全错误的。上述调用符号依赖于keyword arguments;但是,您的函数定义 only 具有位置参数。为了能够支持关键字参数,您需要将函数定义更改为如下所示:

function new_block(; proof::String, previous_hash::String=nothing)
  #                ^ note the semi-colon here
  # ...
end

最后,您将proofprevious_hash 定义为String 类型,但使用1100nothing 调用它们,它们的类型为IntIntVoid

我无法理解您对区块链应用程序的设计选择,但我强烈建议您应该通过更简单的示例逐步学习该语言。例如,如果您只是尝试以下示例,您将了解类型注释在 Julia 中的工作原理:

Main> f(s::String = nothing) = s
f (generic function with 2 methods)

Main> f()
ERROR: MethodError: no method matching f(::Void)
Closest candidates are:
  f(::String) at none:1
  f() at none:1
Stacktrace:
 [1] f() at ./none:1
 [2] eval(::Module, ::Any) at ./boot.jl:235

Main> g(s::String) = s
g (generic function with 1 method)

Main> g(100)
ERROR: MethodError: no method matching g(::Int64)
Closest candidates are:
  g(::String) at none:1
Stacktrace:
 [1] eval(::Module, ::Any) at ./boot.jl:235

Main> h1(var1 = 1, var2 = 100) = var1 + var2
h1 (generic function with 3 methods)

Main> h1(var2 = 5, var1 = 6)
ERROR: function h1 does not accept keyword arguments
Stacktrace:
 [1] kwfunc(::Any) at ./boot.jl:237
 [2] eval(::Module, ::Any) at ./boot.jl:235

最后一条评论是,据我从您的示例中可以看出,您不需要mutable structstruct 应该只是帮助您进行设计 --- 您仍然可以添加/修改其 chaincurrent_transactionsblock 变量。再次检查以下更简单的示例:

Main> struct MyType
         a::Vector{Float64}
       end

Main> m = MyType([1,2,3]);

Main> append!(m.a, 4);

Main> m
MyType([1.0, 2.0, 3.0, 4.0])

您可以将上面示例中的MyTypea 变量视为C++ 术语中的double * const a。您不允许a 更改为指向不同的内存位置,但您可以修改a 指向的内存位置。

简而言之,您应该绝对尝试逐步从官方文档中学习该语言,并在此处发布涉及真正最小示例的问题。从这个意义上说,您的示例令人费解。

【讨论】:

  • 感谢您提供所有这些信息!看起来我对这类项目的理解似乎比我能咀嚼的要多,所以我想我必须仔细看看文档,就像你建议的那样。
  • 你的回答是我在 stackoverflow 上读到的关于 Julia 的最有用的答案!
  • 感谢@PatrickT 的客气话/反馈。考虑到这种动机,我将尝试回答更多问题。
猜你喜欢
  • 1970-01-01
  • 2017-07-17
  • 1970-01-01
  • 2011-03-24
  • 1970-01-01
  • 2017-02-08
  • 1970-01-01
  • 1970-01-01
  • 2013-01-05
相关资源
最近更新 更多