模和余数在Scheme中应该如何工作



我正在阅读R5RS规范,它显示了以下内容:

(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代码应该是什么样的?

这是赤壁的实现:

  • remainder:https://github.com/ashinn/chibi-scheme/blob/12636f4b19e732bcf257ab50808a93c323823099/bignum.c#L1784

  • modulo:https://github.com/ashinn/chibi-scheme/blob/12636f4b19e732bcf257ab50808a93c323823099/lib/init-7.scm#L1403

作者是R7RS的作者之一。

以下是我如何在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编号(即没有bignum(。

如果有人感兴趣,我在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);
}
};
}
})();

您可能对Taylor Campbell的《SRFI 141:整数除法》感兴趣。这是对";。。。提供了一组相当完整的积分除法和余数运算符">

最新更新