LMU ☀️ CMSI 3801
LANGUAGES AND AUTOMATA I
HOMEWORK #2 PARTIAL ANSWERS

Code

Python

from dataclasses import dataclass
from typing import Callable


def first_then_apply(
        strings: list[str],
        predicate: Callable[[str], bool],
        function: Callable[[str], object]) -> object:
    for item in strings:
        if predicate(item):
            return function(item)
    return None


def powers_generator(*, base: int, limit: int):
    power = 1
    while power <= limit:
        yield power
        power *= base


def say(word: str | None = None, /):
    if word is None:
        return ''
    return lambda next=None: word if next is None else say(f'{word} {next}')


def meaningful_line_count(filename: str, /) -> int:
    count = 0
    with open(filename, 'r', encoding='utf-8') as file:
        for line in file:
            line = line.strip()
            if line and not line.startswith('#'):
                count += 1
    return count


@dataclass(frozen=True)
class Quaternion:
    a: float = 0.0
    b: float = 0.0
    c: float = 0.0
    d: float = 0.0

    def __add__(self, q: 'Quaternion') -> 'Quaternion':
        return Quaternion(self.a + q.a, self.b + q.b, self.c + q.c, self.d + q.d)

    def __mul__(self, q: 'Quaternion') -> 'Quaternion':
        return Quaternion(
            q.a * self.a - q.b * self.b - q.c * self.c - q.d * self.d,
            q.a * self.b + q.b * self.a - q.c * self.d + q.d * self.c,
            q.a * self.c + q.b * self.d + q.c * self.a - q.d * self.b,
            q.a * self.d - q.b * self.c + q.c * self.b + q.d * self.a)

    @property
    def coefficients(self) -> tuple[float, float, float, float]:
        return (self.a, self.b, self.c, self.d)

    @property
    def conjugate(self) -> 'Quaternion':
        return Quaternion(self.a, -self.b, -self.c, -self.d)

    def __repr__(self) -> str:
        s = ""
        for c, unit in zip(self.coefficients, ["", "i", "j", "k"]):
            if c == 0:
                continue
            s += '-' if c < 0 else '' if s == '' else '+'
            s += '' if abs(c) == 1 and unit != "" else str(abs(c))
            s += unit
        return '0' if s == '' else s

JavaScript

import { open } from "node:fs/promises"

export function firstThenApply(strings, predicate, fun) {
  const first = strings.find(predicate)
  return first === undefined ? undefined : fun(first)
}

export function* powersGenerator({ ofBase: base, upTo: limit }) {
  for (let power = 1; power <= limit; power *= base) {
    yield power
  }
}

export function say(first) {
  if (first === undefined) {
    return ""
  }
  return (second) => {
    if (second === undefined) {
      return first
    }
    return say(`${first} ${second}`)
  }
}

export async function meaningfulLineCount(filename) {
  let count = 0
  const file = await open(filename, "r")
  for await (const line of file.readLines()) {
    // Note that readLines will autoclose the file, yay
    const trimmed = line.trim()
    if (trimmed && !trimmed.startsWith("#")) {
      count++
    }
  }
  return count
}

export class Quaternion {
  constructor(a, b, c, d) {
    Object.assign(this, { a, b, c, d })
    return Object.freeze(this)
  }
  plus(q) {
    return new Quaternion(
      this.a + q.a,
      this.b + q.b,
      this.c + q.c,
      this.d + q.d
    )
  }
  times(q) {
    return new Quaternion(
      q.a * this.a - q.b * this.b - q.c * this.c - q.d * this.d,
      q.a * this.b + q.b * this.a - q.c * this.d + q.d * this.c,
      q.a * this.c + q.b * this.d + q.c * this.a - q.d * this.b,
      q.a * this.d - q.b * this.c + q.c * this.b + q.d * this.a
    )
  }
  equals(q) {
    return this.a === q.a && this.b === q.b && this.c === q.c && this.d === q.d
  }
  get conjugate() {
    return new Quaternion(this.a, -this.b, -this.c, -this.d)
  }
  get coefficients() {
    return [this.a, this.b, this.c, this.d]
  }
  toString() {
    let s = ""
    for (const [i, c] of this.coefficients.entries()) {
      if (c === 0) continue
      s += c < 0 ? "-" : s == "" ? "" : "+"
      if (Math.abs(c) !== 1 || i === 0) s += Math.abs(c)
      s += ["", "i", "j", "k"][i]
    }
    return s || "0"
  }
}

Lua

function first_then_apply(array, predicate, fun)
  for _, s in ipairs(array) do
    if predicate(s) then
      return fun(s)
    end
  end
  return nil
end

function powers_generator(base, limit)
  local power = 1
  return coroutine.create(function()
    while power <= limit do
      coroutine.yield(power)
      power = power * base
    end
  end)
end

function say(word)
  if word == nil then
    return ""
  end
  return function(next)
    if next == nil then
      return word
    else
      return say(word .. " " .. next)
    end
  end
end

function meaningful_line_count(filename)
  local count = 0
  for line in io.lines(filename) do
    line = line:match "^%s*(.*)"
    if line ~= "" and line:sub(1, 1) ~= "#" then
        count = count + 1
    end
  end
  return count
end

Quaternion = (function (class)
  local meta = {
    __add = function(self, q)
      return class.new(self.a + q.a, self.b + q.b, self.c + q.c, self.d + q.d)
    end,
    __mul = function(self, q)
      return class.new(
        q.a * self.a - q.b * self.b - q.c * self.c - q.d * self.d,
        q.a * self.b + q.b * self.a - q.c * self.d + q.d * self.c,
        q.a * self.c + q.b * self.d + q.c * self.a - q.d * self.b,
        q.a * self.d - q.b * self.c + q.c * self.b + q.d * self.a
      )
    end,
    __eq = function(self, q)
      return self.a == q.a and self.b == q.b and self.c == q.c and self.d == q.d
    end,
    __tostring = function(self)
      local s = ""
      for i, c in ipairs(self:coefficients()) do
        if c ~= 0 then
          s = s .. (c < 0 and "-" or s == "" and "" or "+")
          s = s .. ((i ~= 1 and math.abs(c) == 1) and "" or tostring(math.abs(c)))
          s = s .. ({"", "i", "j", "k"})[i]
        end
      end
      return s == "" and "0" or s
    end,
    __index = {
      coefficients = function(self)
        return {self.a, self.b, self.c, self.d}
      end,
      conjugate = function(self)
        return class.new(self.a, -self.b, -self.c, -self.d)
      end
    },
  }
  class.new = function (a, b, c, d)
    return setmetatable({a = a, b = b, c = c, d = d}, meta)
  end
  return class
end)({})

Exercises

  1. Why is the null reference so hideous?

    Answer: The inventor of the null reference, Tony Hoare, said in 2009 that his invention was so easy to misuse that it has caused a billion dollars of damage up to that time. The very idea of a null reference is a rather disgusting hack. Think about it—the purpose of a reference is to be a value that refers to a value: You start with a value then make a reference to it. A null reference doesn’t reference anything, so where did it come from? What does a reference to nothing even mean? A null reference does not even reference a “nullish object”—it doesn’t reference anything at all. It just doesn’t even make sense. In fact, sadly, when you try to dereference a null reference, you get a runtime error or a system crash which is often difficult to debug. Of course, you can check if the reference is null before dereferencing it, but you know you are going to forget that check somewhere. What are the chances you are going to remember to guard every dereference check? We should not even be in the position of thinking about expecting a value but finding no value.

    There’s more, of course. The article The worst mistake of computer science points out that null (1) subverts types, (2) is sloppy, (3) is a special case, (4) makes poor APIs, (5) exacerbates poor language decisions, (6) is difficult to debug, and (7) is non composable. That’s seven strikes against it.

  2. Why did Tony Hoare introduce the null reference, even though he felt like it was wrong and his friend Edsger Dijkstra said it was a bad idea?

    Answer: It was just soooooooooo easy to implement. Humans often get lazy and love their shortcuts.

  3. In JavaScript 3**35 = 50031545098999710 but in Python 3**35 = 50031545098999707. Which one is right and which is wrong? Why? Explain exactly why the right value is right and the wrong value is that particular wrong value.

    Answer: The Python result is correct because its integer representation is exact up to quantities of many tens of thousands of digits, while the JavaScript result is incorrect as it uses IEEE binary64 floating-point representation, which is only exact for integers up to $2^{53} - 1$. Beyond that, values are only approximations. In particular, if you try to represent 50031545098999707 in binary64, you can’t get it exactly. All you can get is:

    • 436637ed9b2612f3 which is 50031545098999704 in decimal
    • 436637ed9b2612f4 which is 50031545098999712 in decimal

    The computation 3**35 accumulates rounding errors in JavaScript but got close, probably hitting 436637ed9b2612f4. The way JavaScript renders values that need more precision that it has is to use zeros in the places it does not know for sure.

  4. What is the Python equivalent of JavaScript’s {x: 3, [y]: 5, z}?

    Answer: {'x': 3, y: 5, 'z': z}.

  5. Why is it best to not call JavaScript’s == operator “equals” (even though people do it all the time)?

    Answer: You should not call this operator “equals” because it is not transitive. For example, "0x10" == 16, 16 == "16" but "0x10" != "16".

  6. Write a Lua function called arithmeticsequence that accepts two arguments, start and delta, that returns a coroutine such that each time it is resumed it yields the next value in the arithmetic sequence starting with start and incrementing by delta.

    Answer:

    function arithmeticsequence(start, delta)
        return coroutine.create(function()
            local value = start
            while true do
                coroutine.yield(value)
                value = value + delta
            end
        end)
    end
    
  7. What does this script print under (a) static scoping and (b) dynamic scoping?
    var x = 1
    function h() {var x = 9; return g();}
    function f() {return x;}
    function g() {var x = 3; return f();}
    print f() * h() - x
    

    Answer: (a) 0, (b) 2

  8. Why does shallow binding not make much sense when combined with static scoping?

    Answer: Static scoping determines the bindings based on the lexical structure of the code, while shallow binding is all about bindings based on the most recent caller (which isn’t always known statically, at compile time). The two ideas are pretty much in opposition to each other.