약 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.2
및 x = -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 문에서 확인됩니다.
프로그램은 표준 입력에서 한 줄씩 표현식을 읽는 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;
}
}
미래 작업
더 나은 오류 처리, 더 많은 수학 연산자 및 함수, 고정 버퍼 대신 동적 할당을 추가하여 계산기를 자유롭게 확장하십시오. 또 다른 재미있는 연습은 선택적 중위 모드를 추가하는 것입니다. 션트 야드 또는 운영자 우선 순위 알고리즘을 구현합니다.
Reference
이 문제에 관하여(약 100줄의 지그 코드로 된 놀랍도록 유능한 RPN 계산기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://dev.to/stein/a-surprisingly-capable-rpn-calculator-in-about-100-lines-of-zig-code-3bf1
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
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;
}
}
Reference
이 문제에 관하여(약 100줄의 지그 코드로 된 놀랍도록 유능한 RPN 계산기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/stein/a-surprisingly-capable-rpn-calculator-in-about-100-lines-of-zig-code-3bf1텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)