Two Classes

June 28, 2024

Big Macs, fat folks, ecstasy capsules

Presidential scandals, everybody move

记录一次面试失败。现在重写一次,伏惟尚飨。

Class 1

class StateMachine {
  constructor(states, initial) {
    this.states = states
    this.table = new Map() // look up table: <val, State>

    for (let s of states) {
      this.table.set(s.val, s)
    }

    this.initial = initial
  }

  traverse(path) {
    let curr = new Set([this.initial.val])

    for (let step of path) {
      let next_set = new Set()

      for (let start of curr) {
        let start_state = this.table.get(start)
        next_set = next_set.union(start_state.get_next(step)) // node >= 22.0.0
      }

      curr = next_set
    }

    for (let end_state of curr) {
      let state = this.table.get(end_state)
      console.log(`[State ${state.val}] ${state.is_end ? "end" : ""}`)
    }
  }

  print_machine() {
    for (let s of this.states) {
      s.print_path()
    }
  }
}

class State {
  constructor(val) {
    this.val = val
    this.dest = new Map() // <path, Set<state>>
    this.is_end = true
  }

  add_transition(path, state) {
    if (!this.dest.has(path)) {
      this.dest.set(path, new Set())
    }

    this.dest.get(path).add(state)

    if (state !== this.val) {
      this.is_end = false
    }
  }

  get_next(path) {
    if (!this.dest.has(path)) {
      throw "Error: no such path"
    }

    return this.dest.get(path)
  }

  print_path() {
    for (let [k, v] of this.dest.entries()) {
      for (let dest of v) {
        console.log(`[State ${this.val}] ${k} -> ${dest}`)
      }
    }
  }
}

/************ Testing ************/
/*** Setup ***/
let s1 = new State(1)
let s2 = new State(2)
let s3 = new State(3)
let s4 = new State(4)
let s5 = new State(5)

s1.add_transition("a", 1)
s1.add_transition("b", 1)
s1.add_transition("a", 2)
s1.add_transition("b", 4)

s2.add_transition("a", 3)
s2.add_transition("b", 2)

s3.add_transition("a", 3)
s3.add_transition("b", 3)

s4.add_transition("a", 4)
s4.add_transition("b", 5)

s5.add_transition("a", 5)
s5.add_transition("b", 5)

/*** Calls ***/
let sm = new StateMachine([s1, s2, s3, s4, s5], s1)

sm.print_machine()

console.log("========================")

console.log("traverse1: ")
sm.traverse("babaa")

console.log("traverse2: ")
sm.traverse("aaabaaaa")

Class 2

class Shop {
  constructor() {
    this.revenue = 0
    this.cost = 0
    this.stock = new Map() // <name, Stock>
  }

  add_item(name, count, price, expiry) {
    this.cost += count * price

    if (this.stock.has(name)) {
      this.stock.get(name).add(count, expiry)
    } else {
      this.stock.set(name, new Stock(count, expiry))
    }
  }

  sell_item(name, count, price, expiry) {
    if (!this.stock.has(name)) {
      throw "Error: no such item"
    }

    let res = this.stock.get(name).remove(count, expiry)

    if (res) {
      this.revenue += count * price
    } else {
      console.log("Unable to remove")
    }
  }

  print_shop() {
    for (let [k, v] of this.stock.entries()) {
      console.log(`\t${k}:`)
      v.print_stock()
    }
  }
}

class Stock {
  constructor(count, expiry) {
    this.total_count = 0
    this.expiry = new Map()

    this.add(count, expiry)
  }

  add(count, expiry) {
    this.expiry.set(expiry, (this.expiry.get(expiry) || 0) + count)
    this.total_count += count
  }

  remove(count, expiry) {
    // we remove the first count item that's less than expiry date restriction
    if (this.total_count < count) return false

    let stock = [...this.expiry].sort((a, b) => a[0] - b[0])
    let to_be_removed = []

    for (let i = 0; i < stock.length && count > 0; i++) {
      let soon_expired = stock[i]

      if (soon_expired[0] < expiry) {
        continue
      }

      let sell_count = Math.min(count, soon_expired[1])
      count -= sell_count
      to_be_removed.push([
        soon_expired[0],
        sell_count === soon_expired[1] ? Infinity : sell_count,
      ])
    }

    if (count > 0) {
      return false // no way to sell
    }

    // if can sell, update total_count & expiry map
    for (let [date, removed_count] of to_be_removed) {
      if (removed_count === Infinity) {
        this.total_count -= this.expiry.get(date)
        this.expiry.delete(date)
      } else {
        this.total_count -= removed_count
        this.expiry.set(date, this.expiry.get(date) - removed_count)
      }
    }

    return true
  }

  print_stock() {
    console.log(`total: ${this.total_count}`)
    for (let [date, count] of [...this.expiry].sort((a, b) => a[0] - b[0])) {
      console.log(`Expiring in ${date} day:\t ${count}`)
    }
  }
}

/************ Testing ************/
let shop = new Shop()

shop.add_item("apple", 8, 1, 2)
shop.add_item("apple", 3, 1, 7)
shop.add_item("apple", 5, 1, 10)
shop.add_item("banana", 4, 1, 20)
shop.print_shop()

console.log("=====================")

shop.sell_item("apple", 5, 2, 4)
shop.sell_item("apple", 10, 2, 1)
shop.print_shop()