逆ポーランド記法とスタック

# スタック
- Last In, First Outなデータ構造
- 許される操作は`Push`と`Pop`と`Clear`だけ

![](https://upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Lifo_stack.png/350px-Lifo_stack.png)

# 逆ポーランド記法

![](https://slidesplayer.net/slide/11572309/62/images/27/%E9%80%86%E3%83%9D%E3%83%BC%E3%83%A9%E3%83%B3%E3%83%89%E8%A8%98%E6%B3%95+%2A+%E2%86%92+%28%281+2+%2B%29+%283+4+%2B%29+%2A%29+%E2%86%92+%28+3+%283+4+%2B%29+%2A%29.jpg)

> 逆ポーランド記法（ぎゃくポーランドきほう、英語: Reverse Polish Notation, RPN）は、数式やプログラムの記法の一種。演算子を被演算子の後にすることから、後置記法 (Postfix Notation) とも言う。

# スタックを配列で実現する
- スタックを宣言するときに長さを与える必要がある

```go
package main

import (
	"fmt"
	"os"
)

const LENGTH = 10

type Stack struct {
	arr []int
	sp  int
}

func (s *Stack) Clear() {
	s.sp = -1
}

func (s *Stack) Push(val int) {
	if s.sp >= len(s.arr) {
		fmt.Printf("ERROR: stack overflow\n")
		os.Exit(1)
	}
	s.sp += 1         // スタックポインタを上げてから
	s.arr[s.sp] = val // プッシュ
}

func (s *Stack) Pop() int {
	if s.sp <= -1 {
		fmt.Printf("ERROR: stack underflow\n")
		os.Exit(1)
	}
	ret := s.arr[s.sp] // ポップしてから
	s.sp -= 1          // スタックポインタを下げる
	return ret
}

func NewStack(length int) *Stack {
	if length <= 0 {
		fmt.Println("ERROR: length of array should be more than zero")
		os.Exit(1)
	}
	return &Stack{
		arr: make([]int, length),
		sp: -1,
	}
}

func main() {
	input := make([]int, LENGTH)
	for i, _ := range input {
		input[i] = i
	}
	fmt.Println("Input: ", input)
	s := NewStack(LENGTH)
	for ele := range input {
		s.Push(ele)
	}
	for i := 0; i < LENGTH; i++ {
		fmt.Println("Pop: ", s.Pop())
	}
}
```

# スタックをリストで実現する
- スタックを宣言するときに長さが不要

```go
package main

import (
	"fmt"
	"os"
)

type Cell struct {
	val int
	prev *Cell
	next *Cell
}

type Stack struct {
	sp *Cell
}

func (s *Stack) Clear() {
	s.sp = nil
}

func (s *Stack) Pop() int {
	if s.sp == nil {
		fmt.Println("ERROR: no value in stack")
		os.Exit(1)
	}
	ret := s.sp.val
	s.sp = s.sp.prev
	return ret
}

func (s *Stack) Push(val int) {
	fmt.Println("PUSH: ", val)
	c := NewCell(val)
	if s.sp == nil {
		s.sp = c
		return
	}
	s.sp.next = c
	c.prev = s.sp
	s.sp = c
}

func NewCell(val int) *Cell {
	return &Cell{val: val, prev: nil, next: nil}
}

func NewStack() *Stack {
	return &Stack{sp: nil}
}

func main() {
	s := NewStack()
	s.Push(3)
	s.Push(4)
	s.Push(5)
	fmt.Println("Pop: ", s.Pop())
	fmt.Println("Pop: ", s.Pop())
	fmt.Println("Pop: ", s.Pop())
}
```

# 逆ポーランド記法で書かれた式をスタックを用いて評価する

```go
package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"
)

type Stack struct {
	arr []int
	sp  int
}

func (s *Stack) Clear() {
	s.sp = -1
}

func (s *Stack) Push(val int) {
	if s.sp >= len(s.arr) {
		fmt.Printf("ERROR: stack overflow\n")
		os.Exit(1)
	}
	s.sp += 1         // スタックポインタを上げてから
	s.arr[s.sp] = val // プッシュ
}

func (s *Stack) Pop() int {
	if s.sp <= -1 {
		fmt.Printf("ERROR: stack underflow\n")
		os.Exit(1)
	}
	ret := s.arr[s.sp] // ポップしてから
	s.sp -= 1          // スタックポインタを下げる
	return ret
}

func NewStack(length int) *Stack {
	if length <= 0 {
		fmt.Println("ERROR: length of array should be more than zero")
		os.Exit(1)
	}
	return &Stack{
		arr: make([]int, length),
		sp: -1,
	}
}

func main() {
	target := "2 1 + 3 *"
	input := strings.Split(target, " ")
	s := NewStack(len(input) + 1)
	for _, ele := range input {
		switch ele {
		case "+":
			right := s.Pop()
			left := s.Pop()
			s.Push(left + right)
		case "-":
			right := s.Pop()
			left := s.Pop()
			s.Push(left - right)
		case "*":
			right := s.Pop()
			left := s.Pop()
			s.Push(left * right)
		case "/":
			right := s.Pop()
			left := s.Pop()
			s.Push(left / right)
		default:
			s.Push(atoi(ele))
		}
	}
	fmt.Println(target, " = ", s.Pop())

}

func atoi(a string) int {
	ret, _ := strconv.Atoi(a)
	return ret
}
```

```python
ops = {
  "+": (lambda a, b: a + b),
  "-": (lambda a, b: a - b),
  "*": (lambda a, b: a * b),
  "/": (lambda a, b: a / b)
}

def eval(expression):
  tokens = expression.split()
  stack = []

  for token in tokens:
    if token in ops:
      arg2 = stack.pop()
      arg1 = stack.pop()
      result = ops[token](arg1, arg2)
      stack.append(result)
    else:
      stack.append(int(token))

  return stack.pop()
  
print(eval("1 2 + "))
print(eval("990 1 2 + *"))
print(eval("1000 990 1 2 + * +"))
```

暇なときに普通の式を逆ポーランド記法にコンパイルするプログラムを書く。