challenge 法演算

ここでいう法演算とは,与えられた数(ここでは「法」と言います)で剰余をとりながら行う計算のことです.たとえば,法が10である場合,以下のように計算します.

  • 足し算
    • 1 + 2 = 3
    • 7 + 3 = 0 (10を10で割った余りは0)
    • 11 + 12 = 1 + 2 = 3
  • 引き算
    • 3 - 2 = 1
    • 2 - 3 = 9
  • 掛け算
    • 2 * 3 = 6
    • 11 * 12 = 1 * 2 = 2
    • 18 * 39 = 8 * 9 = 2

式と法を与えたときに,このような法演算を行い,計算結果を表示するプログラムを作成してください.

注意点

  • プログラムの入力には,式と法が与えられます.
    • 式に現れる数は,整数のみと仮定してかまいません.しかし,法より大きな数が与えられるかもしれませんし,負の数が与えられるかもしれません.
    • 法は2以上の正整数のみが与えられます.
    • 式と法は,プログラムにとって都合のよい形式で与えられると仮定してかまいません.ソースコード中に埋め込んでしまってもかまいません.
  • 足し算,引き算,掛け算に対応してください.
    • 法10の世界においては,1 - 2 と 1 + 8 は同じ意味です.引き算の計算においては,この性質を使い,足し算に変換してから計算してもかまいません.
  • プログラムの出力として,計算結果を表示して下さい.

  • 与えられた式の中に,範囲外の数(負の数や,法の数以上の数)が現れた時には,必ず一度,式全体を正規化し,その結果を表示してから計算を行って下さい.
    • ここでいう「正規化」とは,式の中のすべての項をいったん法で剰余をとり,0以上,法-1以下の範囲になるようにする,ということです
    • 正規化をする際に,引き算を足し算へ変換する処理を一緒に行ってもかまいません.
    • 計算過程で範囲外の数が現れたときには,正規化を行うことが望ましいですが,必ずしも行う必要はありません.(最終的な計算結果が正しければよしとします)

Posted feedbacks - Flatten

Nested Hidden
こんな感じで。とりあえず逆ポーランドで式を渡します。

>hoo 10 1 2 +
-> 3
>hoo 10 7 3 +
-> 0
>hoo 10 11 12 +
-> 3
>hoo 10 3 2 -
-> 1
>hoo 10 2 3 -
-> 9
>hoo 10 2 3 *
-> 6
>hoo 10 11 12 *
-> 2
>hoo 10 18 39 *
-> 2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Collections.Generic;
static class Program {
    static void Main(string[] args) {
        int mod = int.Parse(args[0]);
        Stack<int> stack = new Stack<int>();
        for(int i = 1; i < args.Length; i++) {
            int value = 0;
            if (args[i] == "+" || args[i] == "-" || args[i] == "*") {
                int y = stack.Pop();
                int x = stack.Pop();
                if (args[i] == "+")         value = x + y;
                else if (args[i] == "-")    value = x - y;
                else if (args[i] == "*")    value = x * y;
            }
            else {
                value = int.Parse(args[i]);
            }
            stack.Push((mod + value % mod) % mod);
        }
        Console.WriteLine("-> {0}", stack.Pop());
    }
}

アルゴリズムよりも、言語ごとのイディオムを問うような問題かなと思いました。

用途によると思いますが、式を埋め込むならマクロが手軽です。

  (with-modular m <式> ...)

と書くと<式> 内の演算がmod m で行われます。


 gosh> (with-modular 10 (list (+ 1 2) (+ 7 3) (+ 11 12)))
 (3 0 3)
 gosh> (with-modular 10 (list (- 3 2) (- 2 3)))
 (1 9)
 gosh> (with-modular 10 (list (* 2 3) (* 11 12) (* 18 39)))
 (6 2 2)
 gosh> (with-modular 10 (+ (- 1 2) (* 3 4) (+ 5 6 7) (* 8 9)))
 1


1
2
3
4
5
6
7
8
9
(define-macro (with-modular m . body)
  `(let ((+ (make-modular + ,m))
         (- (make-modular - ,m))
         (* (make-modular * ,m)))
     ,@body))

(define (make-modular op m)
  (lambda xs
    (modulo (apply op (map (cut modulo <> m) xs)) m)))

一方、式を外部から与えたいなら、+, -, * の束縛がmodular arithmeticに置き換わったようなモジュールを用意してその中でevalするという手が使えます。

 gosh> (eval-modular 10 '(+ 2 9))
 1
 gosh> (eval-modular 10 '(- 2 9))
 3
 gosh> (eval-modular 10 '(* 2 9))
 8
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
(define (make-modular op m)
  (lambda xs
    (modulo (apply op (map (cut modulo <> m) xs)) m)))

(define (make-modular-module m)
  (let1 mod (make-module #f)
    (eval `(define + ,(make-modular + m)) mod)
    (eval `(define - ,(make-modular - m)) mod)
    (eval `(define * ,(make-modular * m)) mod)
    mod))

(define (eval-modular m expr)
  (eval expr (make-modular-module m)))

overloadの格好の事例。

Dan the Perl Monger

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package modulo;
use strict;
use warnings;
our $VERSION = 0.01;

use overload
  '0+' => sub { $_[0]->[0] + 0 },
  '+'  => sub { __PACKAGE__->new( $_[0]->[0] + $_[1]->[0], $_[0]->[1] ) },
  '-'  => sub { __PACKAGE__->new( $_[0]->[0] - $_[1]->[0], $_[0]->[1] ) },
  '*'  => sub { __PACKAGE__->new( $_[0]->[0] * $_[1]->[0], $_[0]->[1] ) },
  ;

sub import {
    my ( $pkg, undef, $mod ) = @_;
    $mod >= 2 or die 'usage: use ', __PACKAGE__, ' mod => n;';
    overload::constant integer => sub { __PACKAGE__->new( shift, $mod ) };
}

sub new {
    my $pkg = shift;
    my ( $n, $m ) = @_;
    $n %= $m;
    bless [ $n, $m ], $pkg;
}

1;

#!/usr/local/bin/perl
use strict;
use warnings;
sub say { print @_, "\n" }
say "1 + 2 = ", 1 + 2;
say "3 - 4 = ", 3 - 4;
say "5 * 6 = ", 5 * 6;
{
    say "# mod = 2";
    use modulo mod => 2;
    say "1 + 2 = ", 1 + 2;
    say "3 - 4 = ", 3 - 4;
    say "5 * 6 = ", 5 * 6;
}
{
    say "# mod = 3";
    use modulo mod => 3;
    say "1 + 2 = ", 1 + 2;
    say "3 - 4 = ", 3 - 4;
    say "5 * 6 = ", 5 * 6;
}

say "# original scope";
say "1 + 2 = ", 1 + 2;
say "3 - 4 = ", 3 - 4;
say "5 * 6 = ", 5 * 6;

shiroさんの#4930の真似で自分も演算子の上書きにしてみました。
with-modで囲んだ部分に指定した数字の法が適用されます。
(with-mod 10
  (print (= (print (* 18 39))
            (print (* 8 9)))))
=>
2
2
T
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
(defmacro with-mod (div &body body)
  `(%with-mod ((+ - * /) ,div) ,@body))

(defmacro %with-mod (((&rest fns) div) &body body)
  (let ((g (gensym)))
    `(let ((,g (labels ,(mapcar
                         (lambda (fn)
                           `(,fn (&rest expr) 
                                 (mod (apply (symbol-function ',fn) expr) ,div)))
                         fns)
                 ,@body)))
       (if (numberp ,g) (mod ,g ,div) ,g))))

まるでどこかの教科書から拾ってきたような。

出力:

(1 + 2) = 3
(7 + 3) = 0
(11 + 12) = (1 + 2) = 3
(3 - 2) = 1
(2 - 3) = 9
(2 * 3) = 6
(11 * 12) = (1 * 2) = 2
(18 * 39) = (8 * 9) = 2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
data AExp = Val Integer | Add AExp AExp | Sub AExp AExp | Mul AExp AExp

isNormal :: Integer -> AExp -> Bool
isNormal m (Val n) = 0 <= n && n < m
isNormal m (Add e1 e2) = isNormal m e1 && isNormal m e2
isNormal m (Sub e1 e2) = isNormal m e1 && isNormal m e2
isNormal m (Mul e1 e2) = isNormal m e1 && isNormal m e2

normalizeExp :: Integer -> AExp -> AExp
normalizeExp m (Val n) = Val $ mod n m
normalizeExp m (Add e1 e2) = Add (normalizeExp m e1) (normalizeExp m e2)
normalizeExp m (Sub e1 e2) = Sub (normalizeExp m e1) (normalizeExp m e2)
normalizeExp m (Mul e1 e2) = Mul (normalizeExp m e1) (normalizeExp m e2)

showExp :: AExp -> String
showExp (Val n) = show n
showExp (Add e1 e2) = "(" ++ showExp e1 ++ " + " ++ showExp e2 ++ ")"
showExp (Sub e1 e2) = "(" ++ showExp e1 ++ " - " ++ showExp e2 ++ ")"
showExp (Mul e1 e2) = "(" ++ showExp e1 ++ " * " ++ showExp e2 ++ ")"

isReduced :: AExp -> Bool
isReduced (Val n) = True
isReduced _ = False

wrapBinOp :: (Integer -> Integer -> Integer) -> AExp -> AExp -> AExp
wrapBinOp op (Val x) (Val y) = Val $ op x y

evalExp :: AExp -> AExp
evalExp e@(Val n) = e
evalExp (Add e1 e2) = wrapBinOp (+) (evalExp e1) (evalExp e2)
evalExp (Sub e1 e2) = wrapBinOp (-) (evalExp e1) (evalExp e2)
evalExp (Mul e1 e2) = wrapBinOp (*) (evalExp e1) (evalExp e2)

evalExpMod :: Integer -> AExp -> AExp
evalExpMod m e = normalizeExp m $ evalExp e

evalPrintExp :: Integer -> AExp -> IO ()
evalPrintExp m e = do
    putStr $ showExp e
    if isNormal m e
        then putStr $ " = " ++ showExp (evalExpMod m e)
        else let e' = normalizeExp m e in
             do putStr $ " = " ++ showExp e'
                putStr $ " = " ++ showExp (evalExpMod m e)
    putStr "\n"

tests = [Add (Val 1) (Val 2), Add (Val 7) (Val 3), Add (Val 11) (Val 12),
         Sub (Val 3) (Val 2), Sub (Val 2) (Val 3),
         Mul (Val 2) (Val 3), Mul (Val 11) (Val 12), Mul (Val 18) (Val 39)]

main = doTest tests
 where doTest [] = return ()
       doTest (t:ts) = evalPrintExp 10 t >> doTest ts

なんか効率悪そうで気に入りませんが・・・

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import operator
import sys

def solve(expr, law):
    sys.stdout.write(expr)
    tokens = []
    normalized = False
    for s in expr.split():
        if s in ('*', '+', '-'):
            tokens.append(s)
        else:
            old = int(s)
            new = old % law
            if old != new:
                normalized = True
            tokens.append(new)
    if normalized:
        sys.stdout.write(" = " + " ".join(str(x) for x in tokens))
    def execute(op, f):
        while 1:
            try:
                i = tokens.index(op)
            except ValueError:
                break
            tokens[i - 1:i + 2] = [f(tokens[i - 1], tokens[i + 1]) % law]
    execute("*", operator.mul)
    execute("+", operator.add)
    execute("-", operator.sub)
    if not len(tokens) == 1:
        raise ValueError("invalid expression")
    sys.stdout.write(" = %d\n" % tokens[0])

def main():
    # addition
    solve("1 + 2", 10)
    solve("7 + 3", 10)
    solve("11 + 12", 10)
    # substraction
    solve("3 - 2", 10)
    solve("2 - 3", 10)
    # multipcation
    solve("2 * 3", 10)
    solve("11 * 12", 10)
    solve("18 * 39", 10)

if __name__ == '__main__':
    main()

数値がマイナスの場合ってこれでいいのかな。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import re

def f(s, m):
  t1 = re.findall('(-?[1-9]\d*)\s*([\+\-\*])\s*(\-?[1-9]\d*)\s*', s)[0]
  t2 = (str(int(t1[0])%m), t1[1], str(int(t1[2])%m))
  print '%s %s %s = ' % t1 + (('%s %s %s = ' % t2) if t1 != t2 else '') + str(eval(''.join(t2)) % m)

f('1 + 2', 10)
f('7 + 3', 10)
f('11 + 12', 10)
f('3 - 2', 10)
f('2 - 3', 10)
f('2 * 3', 10)
f('11 * 12', 10)
f('18 * 19', 10)

functorで。各演算子は正規化したものを表示して、結果を返すようにしています。

- structure M10 = Modular (val m = 10);
- open M10;
- map (fn x => x) [1 + 2, 7 + 3, 11 + 12];
1 + 2 = 3
7 + 3 = 0
11 + 12 = 1 + 2 = 3
val it = [3,0,3] : int list
- map (fn x => x) [3 - 2, 2 - 3];
3 - 2 = 1
2 - 3 = 9
val it = [1,9] : int list
- map (fn x => x) [2 * 3, 11 * 12, 18 * 39];
2 * 3 = 6
11 * 12 = 1 * 2 = 2
18 * 39 = 8 * 9 = 2
val it = [6,2,2] : int list
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
functor Modular (val m : int) = struct
  infix 7 *
  infix 6 + -

  local
    fun calc f x y s =
      let
        open Int

        val _ = print (toString x ^ " " ^ s ^ " " ^ toString y ^ " = ")
        val x' = x mod m and y' = y mod m
        val _ = if x >= m orelse y >= m then
                  print (toString x' ^ " " ^ s ^ " " ^ toString y' ^ " = ")
                else ()
        val result = f (x', y') mod m
      in
        print (toString result ^ "\n");
        result
      end
  in
    fun x + y = calc Int.+ x y "+"
    fun x - y = calc Int.- x y "-"
    fun x * y = calc Int.* x y "*"
  end

end

他の人の回答を見てからではやる気がでませんでしたが、別の情けないアプローチでやることにしました。
法に支配される数値を「#m数値」のように表現するようにしています。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
(defvar *mod-base* 10)

(defstruct (mod10
             (:constructor mk-mod (v))
             (:print-function 
               (lambda (m s d)
                    (format s "#m~D" (mod10-v m)))))
           ( v 0 :type (mod 10)))
(set-dispatch-macro-character #\# #\m
  #'(lambda (s c1 c2)
      (declare (ignore c1 c2))
      (mk-mod (mod (read-preserving-whitespace s t nil t) *mod-base*))))
(defun mod10-plus (x y)(mk-mod (mod (+ (mod10-v x)(mod10-v y)) *mod-base*)))
(defun mod10-sub  (x y)(mk-mod (mod (- (mod10-v x)(mod10-v y)) *mod-base*)))
(defun mod10-mul  (x y)(mk-mod (mod (* (mod10-v x)(mod10-v y)) *mod-base*)))

(flet
  ((+ (&rest l)
     (if (mod10-p (car l))(reduce #'mod10-plus l)(apply #'+ l)))
   (- (&rest l)
     (if (mod10-p (car l))(reduce #'mod10-sub l) (apply #'- l)))
   (* (&rest l)
     (if (mod10-p (car l))(reduce #'mod10-mul l) (apply #'* l))))

  (format t "~&#m1 + #m2 = ~A~%" (+ #m1 #m2))
  (format t "#m7 + #m3 = ~A~%" (+ #m7 #m3))
  (format t "#m11 + #m12 = ~A~%" (+ #m11 #m12))
  (format t "#m3 - #m2 = ~A~%" (- #m3 #m2))
  (format t "#m2 - #m3 = ~A~%" (- #m2 #m3))
  (format t "#m2 * #m3 = ~A~%" (* #m2 #m3))
  (format t "#m11 * #m12 = ~A~%" (* #m11 #m12))
  (format t "#m18 * #m39 = ~A~%" (* #m18 #m39))
)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module Law = struct
  (*normalize 10 (-1);; =>9 *)
  let normalize m x =
    let x = x mod m in
    if x<0 then m+x else x
    
  (* print 10 (+) '+' 7 3;; print 法 演算 演算文字 演算対象1,2 *)
  let print m f op x y = 
    let norm = normalize m in
    Printf.printf "%d %c %d = " x op y;
    
    let nx,ny = (norm x),(norm y) in
    let answer = (norm (f nx ny)) in
      if nx=x or ny=y
      then print_int answer
      else Printf.printf "%d %c %d = %d" nx op ny answer;
    print_newline ()
    
end;;

(*使用例*)
  let add = Law.print 10 (+) '+' in
    add 1 2;  add 7 3;  add 11 12;;

  let sub = Law.print 10 (-) '-' in
    sub 3 2;  sub 2 3;;

  let ( * ) = Law.print 10 ( * ) '*' in
    2*3;  11*12;  18*39;;

orじゃなくてandだった。


ひょっとして hou("-13 * 11",7)  = -13 * 11 = 1 * 4 = 4 ?
そうすると、例題は 3 - 2 = 3 + 8 = 11 ?
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def hou(s,m)
  print s + " = "
  if (w = s.gsub(/\d+/){$&.to_i % m}) != s
    print w + " = " 
  end
  p eval(w)%m
end

hou("-13 * 11",7) # -13 * 11 = -6 * 4 = 4
hou("7 * 9",7)    # 7 * 9 = 0 * 2 = 0
hou("3 * 4",7)    # 3 * 4 = 5

Squeak Smalltalk で。

式文字列をパース(#parse:class:)して生成されるノードツリーを再帰的にスキャンして正規化。そうして得られた正規化後のノードから、あらためてバイトコードを生成(#generate)し、それをコール(#valueWithReceiver:arguments:)しています。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
| houEval |
houEval := [:hou :expr |
    | node rec result |
    node := Parser new parse: 'DoIt ^', expr class: UndefinedObject.
    rec := [:nd |
        (nd isKindOf: LiteralNode) ifTrue: [nd key: nd key \\ hou].
        (nd isKindOf: MessageNode) ifTrue: [
            rec copy fixTemps value: nd receiver.
            nd arguments do: [:arg | rec copy fixTemps value: arg]]].
    rec copy fixTemps value: node body statements first expr.
    result := (node generate valueWithReceiver: nil arguments: #()) \\ hou.
    expr, ' = ', (node asString allButFirst: 8), ' = ', result printString].

houEval value: 10 value: '1 + 2'.   "=> '1 + 2 = 1 + 2 = 3' "
houEval value: 10 value: '7 + 3'.   "=> '7 + 3 = 7 + 3 = 0' "
houEval value: 10 value: '11 + 12'.   "=> '11 + 12 = 1 + 2 = 3' "
houEval value: 10 value: '3 - 2'.   "=> '3 - 2 = 3 - 2 = 1' "
houEval value: 10 value: '2 - 3'.   "=> '2 - 3 = 2 - 3 = 9' "
houEval value: 10 value: '2 * 3'.   "=> '2 * 3 = 2 * 3 = 6' "
houEval value: 10 value: '11 * 12'.   "=> '11 * 12 = 1 * 2 = 2' "
houEval value: 10 value: '18 * 39'.   "=> '18 * 39 = 8 * 9 = 2' "

依存型風に書いてみました.法は型で与えます.
法を 3 とするなら,Modulo (S(S(S Z))) Int を
法を10 とするなら,Modulo (S(S(S(S(S(S(S(S(S(S Z)))))))))) Int を
ここでは簡単のために法 0 〜 10 に対応する型エイリアスを宣言してあります.

*Modulo> 1+2 :: Modulo10
3
*Modulo> 7+3 :: Modulo10
0
*Modulo> 11+12 :: Modulo10
3
*Modulo> 3-2 :: Modulo10
1
*Modulo> 2-3 :: Modulo10
9
*Modulo> 2*3 :: Modulo10
6
*Modulo> 11*12 :: Modulo10
2
*Modulo> 18*39 :: Modulo10
2
*Modulo> (1-2::Modulo10) == (1+8::Modulo10)
True
*Modulo> 1+2 :: Modulo7
3
*Modulo> 7+3 :: Modulo7
3
*Modulo> 11+12 :: Modulo7
2
*Modulo> 3-2 :: Modulo7
1
*Modulo> 2-3 :: Modulo7
6
*Modulo> 2*3 :: Modulo7
6
*Modulo> 11*12 :: Modulo7
6
*Modulo> 18*39 :: Modulo7
2
*Modulo> (1-2::Modulo7) == (1+8::Modulo7)
False
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{-# LANGUAGE EmptyDataDecls #-}

module Modulo where

data Z
zero :: Z
zero = undefined
data S a

predecessor :: S a -> a
predecessor = undefined

class Nat a where
  toInteger :: a -> Integer

instance Nat Z where
  toInteger _ = 0

instance Nat a => Nat (S a) where
  toInteger = (1 +) . Modulo.toInteger . predecessor

newtype Modulo a b = Modulo (a,b)

instance Eq b => Eq (Modulo a b) where
  Modulo (_,x) == Modulo (_,y) = x == y

instance Show b => Show (Modulo a b) where
  show (Modulo (_,x)) = show x

instance (Nat a, Integral b) => Num (Modulo a b) where
  Modulo (m,x) + Modulo (_,y) = Modulo (m,z)
    where z = (x+y) `mod` fromInteger (Modulo.toInteger m)
  Modulo (m,x) - Modulo (_,y) = Modulo (m,z)
    where z = (x-y) `mod` fromInteger (Modulo.toInteger m)
  Modulo (m,x) * Modulo (_,y) = Modulo (m,z)
    where z = (x*y) `mod` fromInteger (Modulo.toInteger m)
  abs = id
  signum (Modulo (m,x)) = Modulo $ (m,signum x)
  fromInteger n = Modulo (m,z)
    where z = fromInteger (n `mod` Modulo.toInteger m)
          m = undefined :: Nat a => a

type Zero  = Z
type One   = S Zero
type Two   = S One      ; type Modulo2 = Modulo Two Int
type Three = S Two      ; type Modulo3 = Modulo Three Int
type Four  = S Three    ; type Modulo4 = Modulo Four Int
type Five  = S Four     ; type Modulo5 = Modulo Five Int
type Six   = S Five     ; type Modulo6 = Modulo Six Int
type Seven = S Six      ; type Modulo7 = Modulo Seven Int
type Eight = S Seven    ; type Modulo8 = Modulo Eight Int
type Nine  = S Eight    ; type Modulo9 = Modulo Nine Int
type Ten   = S Nine     ; type Modulo10= Modulo Ten Int

整数環での四則演算と言うことで、素直に実装してみました。 題意にはないですが除算(乗算の逆演算)も加えてみました。答えがない場合には 0を返します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
public class Sample115 {
    public enum Operator {
        Plus {
            @Override public String toString() { return "+"; }
            @Override public int calc(int lhs, int rhs) { return lhs + rhs; }
        },
        Minus {
            @Override public String toString() { return "-"; }
            @Override public int calc(int lhs, int rhs) { return lhs - rhs; }
        },
        Times {
            @Override public String toString() { return "*"; }
            @Override public int calc(int lhs, int rhs) { return lhs * rhs; }
        },
        Divide {
            @Override public String toString() { return "/"; }
            @Override public int calc(int lhs, int rhs) { return lhs / rhs; }
            @Override public int operate(int lhs, int rhs, int mod) {
                System.out.format("%d %s %d = ", lhs, toString(), rhs);
                int l = modulo(lhs, mod);
                int r = modulo(rhs, mod);
                if (lhs != l || rhs != r) {
                    System.out.format("%d %s %d = ", l, toString(), r);
                }
                int ret = 0;
                for (int index = 0; index < mod; index++) {
                    if (l == modulo(r * index, mod)) {
                        ret = index;
                        break;
                    }
                }
                System.out.println(ret);
                return ret;
            }
        };

        public abstract int calc(int lhs, int rhs);
        public int operate(int lhs, int rhs, int mod) {
            System.out.format("%d %s %d = ", lhs, toString(), rhs);
            int l = modulo(lhs, mod);
            int r = modulo(rhs, mod);
            if (lhs != l || rhs != r) {
                System.out.format("%d %s %d = ", l, toString(), r);
            }
            int ret = modulo(calc(l, r), mod);
            System.out.println(ret);
            return ret;
        }
        protected int modulo(int val, int mod) {
            int ret = val;
            while (ret < 0) { ret += mod; }
            while (ret >= mod) { ret -= mod; }
            return ret;
        }
    }

    public static int calcModulo(Operator op, int lhs, int rhs, int mod) {
        return op.operate(lhs, rhs, mod);
    }


    public static void main(String[] args) {
        calcModulo(Operator.Plus, 1, 2, 10);
        calcModulo(Operator.Plus, 1, 2, 3);
        calcModulo(Operator.Plus, 7, 3, 10);
        calcModulo(Operator.Plus, 11, 12, 10);
        calcModulo(Operator.Minus, 3, 2, 10);
        calcModulo(Operator.Minus, 2, 3, 10);
        calcModulo(Operator.Times, 2, 3, 10);
        calcModulo(Operator.Times, 11, 12, 10);
        calcModulo(Operator.Times, 18, 39, 10);
        calcModulo(Operator.Divide, 1, 2, 11);
        calcModulo(Operator.Divide, 2, 18, 11);
    }
}

これはおもしろい。適切なinstance定義が与えてあれば、型推論によって1などのリテラルさえも後つけのModulo型と解釈させることができちゃうわけですね。duck typingみたいだなあ。


適当に書いてみました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>

int normalize(int num,int law){
    num%=law;
    if(num<0) num=law+num;
    return num;
}

void calc(char* expression ,int law){
    int num1;
    char c;
    int num2;
    int ret;

    ret=sscanf(expression,"%d %c %d =",&num1,&c,&num2);
    if(ret!=3||(c!='+'&&c!='-'&&c!='*')) {
        printf("数式エラー:%s\n",expression);