【问题标题】:How modulo and remainder should work in Scheme?模数和余数在 Scheme 中应该如何工作?
【发布时间】:2020-03-22 16:23:20
【问题描述】:

我正在阅读 R5RS 规范和it shows this

(modulo 13 4)                   ===>  1
(remainder 13 4)                ===>  1

(modulo -13 4)                  ===>  3
(remainder -13 4)               ===>  -1

(modulo 13 -4)                  ===>  -3
(remainder 13 -4)               ===>  1

(modulo -13 -4)                 ===>  -1
(remainder -13 -4)              ===>  -1

(remainder -13 -4.0)            ===>  -1.0  ; inexact

这是正确的吗?我认为模数和余数仅在减号上有所不同。这里显示(modulo -13 4) 应该返回 3,在 JavaScript 中它返回 1。

计算模数和余数的正确算法是什么?我在 JavaScript 实现中的 Scheme 中需要这个。

我在 quora 找到了此代码。

function modulo(num1, num2) {
  if (num2 === 0 || isNaN(num1) || isNaN(num2)) {
    return NaN;
  }

  var isPositive = num1 >= 0;

  num1 = Math.abs(num1);
  num2 = Math.abs(num2);

  while (num1 >= num2) {
    num1 = num1 - num2;
  }

  return isPositive ? num1 : -num1;
}

但它不像在 R5RS 规范中那样工作,它为 modulo(-13, 4) 返回 -1。我还认为 JavaScript 的 %remainder 相同。如何在 JavaScript 或 Scheme 中实现这两个功能?

我的确切问题是:这两个函数的算法应该是什么样的,或者计算它们的 JavaScript 代码应该是什么样的?

【问题讨论】:

  • 这是正确的吗?是的。如here 中所述,不同的语言对模运算和余数运算的定义不同。
  • @ÓscarLópez 你知道这 3 是正确的还是只是规范中的错字?
  • 没错,只要在任何符合规范的实现中尝试。 Javascript 只是做不同的事情,你必须调整它以匹配 Scheme 的规范。还可以看看这个post,Python 在这方面与 Scheme 类似,它将帮助您了解模数的计算方式和原因以不同的方式计算。
  • @ÓscarLópez 问题是我不明白这应该如何实现,我只看到规范中的示例,没有任何解释。 R5RS ch 6.2.5
  • 您自己的链接提供了解释, 示例。我已经编辑了您的问题以添加更精确的链接,请查看。 :)

标签: javascript scheme modulo r5rs language-implementation


【解决方案1】:

【讨论】:

  • Chibi 中的方案代码会有所帮助 我正在寻找源代码,我检查了 Kawa 和 Guile,其中大部分代码都是宿主语言且难以阅读。
【解决方案2】:

这是我在 Urlang 中实现这些功能的方式:

    (define/export/arity (quotient n m) 
      (Math.floor (/ n m)))

    (define/export/arity (remainder n m)
      (% n m))

    (define/export/arity (quotient/remainder n m)
      (values (quotient n m) (remainder n m)))

    (define/export/arity (modulo n m)
      (var [who (λ() (string->symbol "modulo"))])
      (unless (and (number? n) (not (infinite? n))) 
         (raise-argument-error (who) "integer?" 1 n m))
      (unless (and (number? m) (not (infinite? m))) 
         (raise-argument-error (who) "integer?" 2 n m))
      (% (+ (% n m) m) m))

这里的Math.floor%+ 是标准的JavaScript 函数/操作符。

为了好奇,这里是生成的 JavaScript:

function remainder(n,m){if(!((arguments.length===2)===false))VOID;else (raise_arity_error_m((string__gsymbol("remainder")),2,arguments));;return (n%m);};
function quotient_qremainder(n,m){if(!((arguments.length===2)===false))VOID;else (raise_arity_error_m((string__gsymbol("quotient/remainder")),2,arguments));;return (values((quotient(n,m)),(remainder(n,m))));};
function modulo(n,m){if(!((arguments.length===2)===false))VOID;else (raise_arity_error_m((string__gsymbol("modulo")),2,arguments));;var who=(function(){return (string__gsymbol("modulo"));});(((!((number_p(n))&&(!(infinite_p(n)))))===false)?undefined:((function(){return (raise_argument_error((who()),"integer?",1,n,m));})()));(((!((number_p(m))&&(!(infinite_p(m)))))===false)?undefined:((function(){return (raise_argument_error((who()),"integer?",2,n,m));})()));return (((n%m)+m)%m);};

更新

这里有一点上下文。该代码实现了 Scheme 运行时的余数和模数。运行时是在 Urlang(它是带有 s-expression-syntax 的 JavaScript)中实现的。

从输出的 JavaScript 可以看出:

  • 方案remainder 实现为n%m
  • 方案modulo 实现为((n%m)+m)%m

这假设 Scheme 数字表示为 JavaScript 数字(即没有 bignums)。

【讨论】:

  • 无论是什么语言,Urlang 都不好用。代码有%,在每种语言中,这个运算符的工作方式都不同,在 JavaScript 中 % 不是提醒,所以我不能使用这个实现。任何不使用%的语言我都可以,有JS代码但是被混淆了。
  • @jcubic 我添加了一些上下文。
  • 我认为quotient不正确,如果n为负数,它需要ceil
  • 您的reminder 比方案中的实现要短得多,我认为JS % 不是方案reminder,但似乎它是并且只有modulo 不同。谢谢。
  • 一开始我很困惑 - 我习惯了 C 中的 % 是模数。
【解决方案3】:

如果有人感兴趣,我已经在 Reddit 上问过同样的问题(带有此问题的链接)并得到了确切方案代码的答案:

https://www.reddit.com/r/scheme/comments/fpt1b8/help_with_modulo_and_reminder_in_r5rs/

(define (modulo a b)
  (- a (* b (floor (/ a b)))))

(define (remainder a b)
  (- a (* b (truncate (/ a b)))))

;; as @soegaard show reminder is just JavaScript % so this can be
;; if % is proper function
(define (remainder a b)
  (% a b))

它与 R5RS 中的示例相同:

(list
  (= (modulo 13 4) 1)
  (= (remainder 13 4) 1)      ;; ===>  1

  (= (modulo -13 4) 3)        ;; ===>  3
  (= (remainder -13 4) -1)    ;; ===>  -1

  (= (modulo 13 -4) -3)       ;; ===>  -3
  (= (remainder 13 -4) 1)     ;; ===>  1

  (= (modulo -13 -4) -1)      ;; ===>  -1
  (= (remainder -13 -4) -1)   ;; ===>  -1

  (= (remainder -13 -4.0) -1.0)) ;; ===>  -1.0  ; inexact

floorMath.floortruncate 是:

var truncate = (function() {
    if (Math.trunc) {
        return Math.trunc;
    } else {
        return function(x) {
            if (x === 0) {
                return 0;
            } else if (x < 0) {
                return Math.ceil(x);
            } else {
                return Math.floor(x);
            }
        };
    }
})();

【讨论】:

    猜你喜欢
    • 2016-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-13
    • 2013-12-03
    • 2010-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多