使用元表来添加两种不同类型

huangapple go评论75阅读模式
英文:

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

huangapple
  • 本文由 发表于 2023年7月27日 23:21:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76781256.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定