英文:
Using metatables for adding two different types
问题
我有一个分数表,其中存储了分数,并将分数与其元表关联起来。 fraction.add
是元操作 (__add
)。
这里有一个问题。我想要计算 x + y。
如果 x 和 y 都是分数,那么计算它没有问题。它会使用 fraction.add
来计算。
如果 x 和 y 都是数字,而不是分数,那么它就是简单的数字相加,与分数无关。
如果 x 是分数,而 y 是非分数的数字(或反之),那么我想要将分数 x 转换为数字并将其加到 y 上。当然,我可以轻松地将分数转换为数字,但问题在于与 fraction.add
关联的分数的 __add
元表只在 x 和 y 都是分数时才起作用。因为 y 不是分数,它会引发错误。所以我正在寻找处理一个是分数而另一个是非分数数字的情况的逻辑。这可能只需要在元表中使用简单的 if else,但我不知道如何做。任何想法都将非常有帮助。
英文:
I have fractions table which stores fractions and fractions get associated with its metatable. fraction.add
is metaoperation (__add
).
Here is the problem. I want to evaluate x + y.
If x and y are both fractions then there is no issue in evaluating it. It gets evaluated withfarction.add
.
If both x and y are numbers, which are not fractions, then it is simple addition of numbers, which has nothing to do with fractions.
If x is fraction and y is non-fraction number (or vice-versa), then I want to convert fraction x into number and add it to y. I of course can easily convert fraction into number, but the problem is with __add
metatable of fraction which is associated with fraction.add
. It works only when both x and y are fractions. It throws error as y is not fraction. So I am looking for the logic that will handle the case when one is fraction and other is non-fraction number. It probably is simple if else in metatable, but I am not able to do it. Any ideas would really be helpful.
答案1
得分: 2
I've implemented a full-fledged fraction
"class" with support for this. It's as simple as checking the types of the arguments a, b
of the __add
metamethod and converting them to fractions if needed.
Or, for a more minimal example, which skimps on a couple points:
- Fractions aren't shortened. Float / integer imprecisions / overflows will ultimately result in inaccurate fractions. Ideally you should use some kind of big integers for numerator and denominator, and shorten after every operation.
- For simplicity, only integers will be converted to fraction; floats will raise an error, since converting floats to fractions is more tricky.
- It's missing plenty of operations.
local fraction_mt = {}
local function new_fraction(numerator, denominator)
assert(numerator % 1 == 0)
assert(denominator % 1 == 0)
return setmetatable({numerator = numerator, denominator = denominator}, fraction_mt)
end
local function add_fractions(a, b)
-- Note: No care is taken to shorten the resulting fraction here.
return new_fraction(a.numerator * b.denominator + b.numerator * a.denominator,
a.denominator * b.denominator)
end
function fraction_mt.__add(a, b)
if type(a) == "number" then
a = new_fraction(a, 1)
elseif type(b) == "number" then
b = new_fraction(b, 1)
end
return add_fractions(a, b)
end
function fraction_mt:__tostring()
return ("%d/%d"):format(self.numerator, self.denominator)
end
print(new_fraction(1, 2) + new_fraction(1, 2)) -- 4/4
print(new_fraction(1, 2) + 1) -- 3/2
print(1 + new_fraction(1, 2)) -- 3/2
Note that Lua will call __add
even if only one of the operands is not a number (see the reference manual):
If any operand for an addition is not a number, Lua will try to call a metamethod. It starts by checking the first operand (even if it is a number); if that operand does not define a metamethod for __add, then Lua will check the second operand. If Lua can find a metamethod, it calls the metamethod with the two operands as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, if no metamethod is found, Lua raises an error.
After clarification: To convert a fraction to a number before adding, producing a number for fraction + number
or number + fraction
, simply use:
function fraction_mt:tonumber()
return self.numerator / self.denominator
end
function fraction_mt.__add(a, b)
if type(a) == "number" then
return a + b:tonumber()
end if type(b) == "number" then
return a:tonumber() + b
end
return add_fractions(a, b)
end
英文:
I've implemented a full-fledged such fraction
"class" with support for this. It's as simple as checking the types of the arguments a, b
of the __add
metamethod and converting them to fractions if needed.
Or, for a more minimal example, which skimps on a couple points:
- Fractions aren't shortened. Float / integer imprecisions / overflows will ultimately result in inaccurate fractions. Ideally you should use some kind of big integers for numerator and denominator, and shorten after every operation.
- For simplicity, only integers will be converted to fraction; floats will raise an error, since converting floats to fractions is more tricky.
- It's missing plenty of operations.
local fraction_mt = {}
local function new_fraction(numerator, denominator)
assert(numerator % 1 == 0)
assert(denominator % 1 == 0)
return setmetatable({numerator = numerator, denominator = denominator}, fraction_mt)
end
local function add_fractions(a, b)
-- Note: No care is taken to shorten the resulting fraction here.
return new_fraction(a.numerator * b.denominator + b.numerator * a.denominator,
a.denominator * b.denominator)
end
function fraction_mt.__add(a, b)
if type(a) == "number" then
a = new_fraction(a, 1)
elseif type(b) == "number" then
b = new_fraction(b, 1)
end
return add_fractions(a, b)
end
function fraction_mt:__tostring()
return ("%d/%d"):format(self.numerator, self.denominator)
end
print(new_fraction(1, 2) + new_fraction(1, 2)) -- 4/4
print(new_fraction(1, 2) + 1) -- 3/2
print(1 + new_fraction(1, 2)) -- 3/2
Note that Lua will call __add
even if only one of the operands is not a number (see the reference manual):
> If any operand for an addition is not a number, Lua will try to call a metamethod. It starts by checking the first operand (even if it is a number); if that operand does not define a metamethod for __add, then Lua will check the second operand. If Lua can find a metamethod, it calls the metamethod with the two operands as arguments, and the result of the call (adjusted to one value) is the result of the operation. Otherwise, if no metamethod is found, Lua raises an error.
After clarification: To convert a fraction to a number before adding, producing a number for fraction + number
or number + fraction
, simply use:
function fraction_mt:tonumber()
return self.numerator / self.denominator
end
function fraction_mt.__add(a, b)
if type(a) == "number" then
return a + b:tonumber()
end if type(b) == "number" then
return a:tonumber() + b
end
return add_fractions(a, b)
end
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论