在 Crystal 宏中为新变量赋值
Assigning a value to a new variable in a Crystal macro
我想在宏中创建一个新变量。我的代码可以减少到:
macro test()
%p = "a = 3"
%p
end
test()
puts(a)
但我收到一个错误 undefined local variable or method 'a'
我试图将 %p
包装在 {{ }}
或 {% %}
中,但它无法编译 ("unexpected token: %
")
编辑
这是一些背景信息。
我经常不得不使用带有一些公共字段的两个命名元组,然后将其转换为 JSON 并发送给不同的客户端。
a = {x: xx, y: yy, w: ww}
b = {x: xx, y: yy, z: zz}
目前我正在重复写这两篇文章。 ww、xx、yy、zz 的值在编译时未知。我想用在编译时调用进行合并的宏来替换它。
我想出了以下代码来创建我当前手动输入的代码行:
macro merge(common, aOnly, bOnly)
# Start, middle and end parts of target instructions
%p1a = "a = {"
%p1b = "b = {"
%p2 = ""
%p2a = ""
%p2b = ""
%p3 = "}"
# Creating the middle part
{% for key, value in common %}
%p2 = %p2 + "{{key.id}}" + ": " + "{{value}}" + ','
{% end %}
%p2a = %p2
{% for key, value in aOnly %}
%p2a = %p2a + "{{key.id}}" + ": " + "{{value}}" + ','
{% end %}
%p2b = %p2
{% for key, value in bOnly %}
%p2b = %p2b + "{{key.id}}" + ": " + "{{value}}" + ','
{% end %}
# Removing unneeded comma
%p2a = %p2a.chomp(",")
%p2b = %p2b.chomp(",")
# Display
puts(%p1a + %p2a + %p3)
puts(%p1b + %p2b + %p3)
# Definition of new variables
%p1a + %p2a + %p3
%p1b + %p2b + %p3
end
merge({x: xx, y: yy}, {w: ww}, {z: zz})
这里的主要问题是对%names
的误解。这不是使指令编译时间的构造。写 %a = 5
和写 guaranteed_unique_name = 5
是一样的——这是一种避免干扰可能存在于同一范围内的其他变量的方法,但它仍然是正常的运行时变量赋值。
构造字符串然后在编译时输出它们不是使用宏的可行方法,而且它几乎总是不可能结束,因为您不能将不同的值重新分配给宏变量。但这里有一个例子,如果它确实有效,它可能会是什么样子,仍然尝试将你的方法与字符串一起使用:
macro merge(common, aOnly, bOnly)
# Start, middle and end parts of target instructions
{%
p1a = "a = {"
p1b = "b = {"
p2 = ""
p2a = ""
p2b = ""
p3 = "}"
%}
# Creating the middle part
{% for key, value in common %}
{% p2 = p2 + "#{key.id}: #{value}," %}
{% end %}
{% p2a = p2 %}
{% for key, value in aOnly %}
{% p2a += "#{key.id}: #{value}," %}
{% end %}
{% p2b = p2 %}
{% for key, value in bOnly %}
{% p2b += "#{key.id}: #{value}," %}
{% end %}
# Removing unneeded comma
{% p2a = p2a.chomp(",") %}
{% p2b = p2b.chomp(",") %}
# Definition of new variables
{{(p1a + p2a + p3).id}}
{{(p1b + p2b + p3).id}}
end
merge({x: xx, y: yy}, {w: ww}, {z: zz})
现在我将使用更常用的结构重写您的示例:
macro merge(common, a_only, b_only)
a = {
{% for key, value in common %}
{{key}}: {{value}},
{% end %}
{% for key, value in a_only %}
{{key}}: {{value}},
{% end %}
}
b = {
{% for key, value in common %}
{{key}}: {{value}},
{% end %}
{% for key, value in b_only %}
{{key}}: {{value}},
{% end %}
}
end
merge({x: "a", y: 5}, {w: "b"}, {z: 7})
pp a, b
如您所见,放在宏 body 中的普通代码是运行时代码,而 {% %}
和 {{ }}
中的代码是在编译时执行的。您可以制作编译时循环并将它们与普通代码行交织在一起,以自然地创建复杂代码。
希望这回答了问题标题"Assigning a value to a new variable in a Crystal macro":将一些东西赋值给运行时变量,你做的事情与在宏中输出任何运行时代码一样——你只需把它写出来,比如a = 5
,因为默认情况下宏输出运行时代码,并且只有 {%
和 {{
分隔编译时构造。
为了回复评论,这里可能是一个更惯用的版本,它甚至说明了 temporary/unique 变量名。它使用它们来创建元组,然后宏执行的结果是一个可以方便地解包的元组,而不是依赖副作用,这种情况很少发生。
macro merge(common, a_only, b_only)
%a = {
{% for key, value in common %}
{{key}}: {{value}},
{% end %}
{% for key, value in a_only %}
{{key}}: {{value}},
{% end %}
}
%b = {
{% for key, value in common %}
{{key}}: {{value}},
{% end %}
{% for key, value in b_only %}
{{key}}: {{value}},
{% end %}
}
{ %a, %b }
end
first, second = merge({x: "a", y: 5}, {w: "b"}, {z: 7})
puts first
我想在宏中创建一个新变量。我的代码可以减少到:
macro test()
%p = "a = 3"
%p
end
test()
puts(a)
但我收到一个错误 undefined local variable or method 'a'
我试图将 %p
包装在 {{ }}
或 {% %}
中,但它无法编译 ("unexpected token: %
")
编辑
这是一些背景信息。
我经常不得不使用带有一些公共字段的两个命名元组,然后将其转换为 JSON 并发送给不同的客户端。
a = {x: xx, y: yy, w: ww}
b = {x: xx, y: yy, z: zz}
目前我正在重复写这两篇文章。 ww、xx、yy、zz 的值在编译时未知。我想用在编译时调用进行合并的宏来替换它。
我想出了以下代码来创建我当前手动输入的代码行:
macro merge(common, aOnly, bOnly)
# Start, middle and end parts of target instructions
%p1a = "a = {"
%p1b = "b = {"
%p2 = ""
%p2a = ""
%p2b = ""
%p3 = "}"
# Creating the middle part
{% for key, value in common %}
%p2 = %p2 + "{{key.id}}" + ": " + "{{value}}" + ','
{% end %}
%p2a = %p2
{% for key, value in aOnly %}
%p2a = %p2a + "{{key.id}}" + ": " + "{{value}}" + ','
{% end %}
%p2b = %p2
{% for key, value in bOnly %}
%p2b = %p2b + "{{key.id}}" + ": " + "{{value}}" + ','
{% end %}
# Removing unneeded comma
%p2a = %p2a.chomp(",")
%p2b = %p2b.chomp(",")
# Display
puts(%p1a + %p2a + %p3)
puts(%p1b + %p2b + %p3)
# Definition of new variables
%p1a + %p2a + %p3
%p1b + %p2b + %p3
end
merge({x: xx, y: yy}, {w: ww}, {z: zz})
这里的主要问题是对%names
的误解。这不是使指令编译时间的构造。写 %a = 5
和写 guaranteed_unique_name = 5
是一样的——这是一种避免干扰可能存在于同一范围内的其他变量的方法,但它仍然是正常的运行时变量赋值。
构造字符串然后在编译时输出它们不是使用宏的可行方法,而且它几乎总是不可能结束,因为您不能将不同的值重新分配给宏变量。但这里有一个例子,如果它确实有效,它可能会是什么样子,仍然尝试将你的方法与字符串一起使用:
macro merge(common, aOnly, bOnly)
# Start, middle and end parts of target instructions
{%
p1a = "a = {"
p1b = "b = {"
p2 = ""
p2a = ""
p2b = ""
p3 = "}"
%}
# Creating the middle part
{% for key, value in common %}
{% p2 = p2 + "#{key.id}: #{value}," %}
{% end %}
{% p2a = p2 %}
{% for key, value in aOnly %}
{% p2a += "#{key.id}: #{value}," %}
{% end %}
{% p2b = p2 %}
{% for key, value in bOnly %}
{% p2b += "#{key.id}: #{value}," %}
{% end %}
# Removing unneeded comma
{% p2a = p2a.chomp(",") %}
{% p2b = p2b.chomp(",") %}
# Definition of new variables
{{(p1a + p2a + p3).id}}
{{(p1b + p2b + p3).id}}
end
merge({x: xx, y: yy}, {w: ww}, {z: zz})
现在我将使用更常用的结构重写您的示例:
macro merge(common, a_only, b_only)
a = {
{% for key, value in common %}
{{key}}: {{value}},
{% end %}
{% for key, value in a_only %}
{{key}}: {{value}},
{% end %}
}
b = {
{% for key, value in common %}
{{key}}: {{value}},
{% end %}
{% for key, value in b_only %}
{{key}}: {{value}},
{% end %}
}
end
merge({x: "a", y: 5}, {w: "b"}, {z: 7})
pp a, b
如您所见,放在宏 body 中的普通代码是运行时代码,而 {% %}
和 {{ }}
中的代码是在编译时执行的。您可以制作编译时循环并将它们与普通代码行交织在一起,以自然地创建复杂代码。
希望这回答了问题标题"Assigning a value to a new variable in a Crystal macro":将一些东西赋值给运行时变量,你做的事情与在宏中输出任何运行时代码一样——你只需把它写出来,比如a = 5
,因为默认情况下宏输出运行时代码,并且只有 {%
和 {{
分隔编译时构造。
为了回复评论,这里可能是一个更惯用的版本,它甚至说明了 temporary/unique 变量名。它使用它们来创建元组,然后宏执行的结果是一个可以方便地解包的元组,而不是依赖副作用,这种情况很少发生。
macro merge(common, a_only, b_only)
%a = {
{% for key, value in common %}
{{key}}: {{value}},
{% end %}
{% for key, value in a_only %}
{{key}}: {{value}},
{% end %}
}
%b = {
{% for key, value in common %}
{{key}}: {{value}},
{% end %}
{% for key, value in b_only %}
{{key}}: {{value}},
{% end %}
}
{ %a, %b }
end
first, second = merge({x: "a", y: 5}, {w: "b"}, {z: 7})
puts first