약 100줄의 지그 코드로 된 놀랍도록 유능한 RPN 계산기

30777 단어 zig
요약:

  • Reverse polish notation 굉장하다

  • Zig 굉장하다

  • 이차 방정식을 풀고 코드를 살펴보기 전에 2 + 2가 여전히 4인지 살펴보겠습니다.

    2 2 +
    M0 = 4
    


    휴! 모든 답변이 다음 사용 가능한 메모리 슬롯에 어떻게 배치되는지 확인하십시오. 이것은 이차 방정식에 대한 두 가지 해를 계산하는 다음 예에서 유용할 것입니다.

    먼저 알려진 값을 입력하여 상수를 반복하지 않고 참조할 수 있는 메모리 슬롯을 얻습니다. 따라서 2차 공식에서, b 및 c 대신 M0, M1 및 M2를 사용합니다.

    $zig run rpn.zig
    
    # You can place comments using the hash sign.
    # Let's solve the quadratic equation where a=5, b=6, c=1 
    5
    M0 = 5
    6
    M1 = 6
    1
    M2 = 1
    
    # First solution
    M1 1 ~ * M1 2 ^ 4 M0 * M2 * -  + 2 M0 * /
    M3 = -0.2
    
    # Second solution
    M1 1 ~ * M1 2 ^ 4 M0 * M2 * -  - 2 M0 * /
    M4 = -1
    


    거기에 솔루션은 x = -0.2x = -1입니다.

    그럼에도 불구하고 왜 RPN인가? 입력할 내용이 적고(괄호가 필요하지 않기 때문에) 일부 팬은 실수가 적다고 주장합니다. 그러나 이 데모의 주된 이유는 RPN 계산기를 구현하는 것이 정말 간단하기 때문입니다. 사소한 토큰화/숫자로 변환하는 것 외에는 구문 분석이 필요하지 않으며 연산자 우선 순위는 문제가 되지 않습니다.

    구의 부피를 계산하는 예를 하나 더 들어보겠습니다. 먼저 반경을 메모리 슬롯에 넣은 다음 공식을 평가합니다.

    4.8
    M0 = 4.8
    
    4 3 / 𝜋 * M0 3 ^ *
    M1 = 463.2466863277365
    


    Here's a nice little tool infix에서 RPN으로 변환하는 데 도움이 필요한 경우.

    지원되는 연산자 및 함수



    이것이 지금까지 구현된 것입니다. 더 많은 함수와 연산자에 대한 지원을 추가하는 것은 쉽습니다. 토크나이저 루프에서 switch 문을 업데이트하기만 하면 됩니다.


    작전
    설명


    */+ - %
    기본 산술/모듈러스

    ^^
    지수화

    ~
    부정

    중...
    M0과 같은 메모리 참조

    죄, 코사인, 황갈색
    삼각 함수

    평방 미터 또는 √
    제곱근

    파이 또는 𝜋
    스택에 𝜋 푸시

    이자형
    오일러의 수를 스택에 푸시

    평균
    스택에서 모든 숫자를 팝하고 평균을 푸시합니다.


    또한 mc를 입력하여 메모리 슬롯을 지우고 exit를 입력하여 계산기를 종료할 수 있습니다.

    256개의 슬롯을 사용할 수 있으며 세션에서 더 많은 표현식을 평가하면 이 슬롯이 바뀝니다.

    어떻게 작동합니까?



    프로그램은 표준 입력에서 한 줄씩 표현식을 읽는 REPL(읽기 평가 인쇄 루프)을 설정합니다.

    그런 다음 각 줄은 공간에서 분할됩니다. 그런 다음 각 결과 토큰은 switch 문에서 확인됩니다.
  • 숫자인 경우 평가 스택으로 푸시합니다.
  • 단항 연산자/함수인 경우 스택의 맨 위는 연산자를 적용한 후 값으로 대체됩니다.
  • 곱셈과 같은 이항 연산자인 경우 스택에서 두 개의 숫자를 꺼내 결과로 대체합니다.
  • 메모리 참조인 경우 해당 값이 스택에 푸시됨
  • 빈 줄이나 주석이면 다음 줄을 읽으십시오.

  • 최종 결과는 사용 가능한 다음 메모리 슬롯에 저장되고 인쇄됩니다.

    코드



    오른쪽 상단 모서리에 있는 확장 아이콘을 클릭합니다.

    const std = @import("std");
    
    pub fn main() !void {
        const stdin = std.io.getStdIn().reader();
        const stdout = std.io.getStdOut().writer();
        try stdout.print("Zig RPN Calculator\nType mc to clear memory slots, exit to quit.\n", .{});
    
        var repl: [1024]u8 = undefined;
        var memslot = [_]f64{0} ** 256;
        var memslot_index: usize = 0;
    
        repl_loop: while (true) {
            const line = try stdin.readUntilDelimiterOrEof(&repl, '\n');
            var input = std.mem.trimRight(u8, line.?, "\r\n");
            if (std.mem.eql(u8, input, "")) {
                continue;
            }
            else if (std.mem.eql(u8, input, "exit")) {
                return;
            }
            else if (std.mem.eql(u8, input, "mc")) {
                memslot_index = 0;
                continue;
            }
    
            var it = std.mem.split(input, " ");
    
            var stack = [_]f64{0} ** 256;
            var sp: usize = 0;
            while (it.next()) |tok| {
                if (tok.len == 0) {
                    continue :repl_loop;
                }
                switch (tok[0]) {
                    '*' => {stack[sp-2] = stack[sp-2] * stack[sp-1]; sp -= 1;},
                    '/' => {stack[sp-2] = stack[sp-2] / stack[sp-1]; sp -= 1;},
                    '+' => {stack[sp-2] = stack[sp-2] + stack[sp-1]; sp -= 1;},
                    '-' => {stack[sp-2] = stack[sp-2] - stack[sp-1]; sp -= 1;},
                    '^' => {stack[sp-2] = std.math.pow(f64, stack[sp-2], stack[sp-1]); sp -= 1;},
                    '%' => {stack[sp-2] = try std.math.mod(f64, stack[sp-2], stack[sp-1]); sp -= 1;},
                    '~' => {stack[sp-1] = -stack[sp-1];},
                    '#' => continue :repl_loop,
                    'M' => {
                        const index = std.fmt.parseInt(usize, tok[1..], 10) catch |err| { 
                            std.debug.print("Invalid memory memory slot index in {s}\n", .{tok}); continue :repl_loop;
                        };
                        stack[sp] = memslot[index % 256];
                        sp += 1;
                    },
                    '0'...'9', '.' => {
                        const num = std.fmt.parseFloat(f64, tok) catch |err| {
                            std.debug.print("Invalid character in floating point number {s}\n", .{tok}); continue :repl_loop;
                        };
                        stack[sp] = num;
                        sp += 1;
                    },
                    else => {
                        if (std.mem.eql(u8, tok, "sin")) {
                            stack[sp-1] = std.math.sin(stack[sp-1]);
                        }
                        else if (std.mem.eql(u8, tok, "cos")) {
                            stack[sp-1] = std.math.cos(stack[sp-1]);
                        }
                        else if (std.mem.eql(u8, tok, "tan")) {
                            stack[sp-1] = std.math.tan(stack[sp-1]);
                        }
                        else if (std.mem.eql(u8, tok, "sqrt") or std.mem.eql(u8, tok, "√")) {
                            stack[sp-1] = std.math.sqrt(stack[sp-1]);                                      
                        }
                        else if (std.mem.eql(u8, tok, "pi") or std.mem.eql(u8, tok, "𝜋")) {
                            stack[sp] = std.math.pi;
                            sp += 1;
                        }
                        else if (std.mem.eql(u8, tok, "e")) {
                            stack[sp] = std.math.e;
                            sp += 1;
                        }                                                    
                        else if (std.mem.eql(u8, tok, "avg")) {
                            const count = sp;
                            var sum: f64 = 0;
                            for (stack) |val| {
                                sum += val;
                            }
                            sp-=count;
                            stack[sp] = sum / @intToFloat(f64, count);
                            sp+=1;
                        }
                        else {
                            try stdout.print("Invalid operation {s}\n", .{tok});                        
                            continue :repl_loop;
                        }
                    },
                }
            }
            memslot[memslot_index % 256] = stack[sp-1];
            try stdout.print("M{d} = {d}\n", .{memslot_index % 256, stack[sp-1]});
            memslot_index += 1;
        }
    }
    


    미래 작업



    더 나은 오류 처리, 더 많은 수학 연산자 및 함수, 고정 버퍼 대신 동적 할당을 추가하여 계산기를 자유롭게 확장하십시오. 또 다른 재미있는 연습은 선택적 중위 모드를 추가하는 것입니다. 션트 야드 또는 운영자 우선 순위 알고리즘을 구현합니다.

    좋은 웹페이지 즐겨찾기