exercises.ml
exception Negative_Amount
let change (amount: int) =
if amount < 0 then
raise Negative_Amount
else
let denominations = [25; 10; 5; 1] in
let rec aux remaining denominations =
match denominations with
| [] -> []
| d :: ds -> (remaining / d) :: aux (remaining mod d) ds
in
aux amount denominations
let first_then_apply (array: 'a list) (predicate: 'a -> bool) (consumer: 'a -> 'b option) =
match List.find_opt predicate array with
| Some x -> consumer x
| None -> None
(*
This also works:
List.find_opt predicate array |> Option.map consumer |> Option.join
*)
let powers_generator base =
let rec gen_from power () =
Seq.Cons (power, gen_from (power * base))
in
gen_from 1
let meaningful_line_count filename =
let is_meaningful_line line =
let trimmed = String.trim line in
trimmed <> "" && not (String.starts_with ~prefix:"#" trimmed)
in
let ic = open_in filename in
let finally () = close_in ic in
let rec count_lines count =
match input_line ic with
| line ->
let new_count = if is_meaningful_line line then count + 1 else count in
count_lines new_count
| exception End_of_file ->
count
in
Fun.protect ~finally (fun () -> count_lines 0)
type shape =
| Sphere of float
| Box of float * float * float
let volume s =
match s with
| Sphere r -> (4.0 /. 3.0) *. Float.pi *. (r ** 3.0)
| Box (w, l, d) -> w *. l *. d
let surface_area s =
match s with
| Sphere r -> 4.0 *. Float.pi *. (r ** 2.0)
| Box (w, l, d) -> 2.0 *. ((w *. l) +. (w *. d) +. (l *. d))
type 'a binary_search_tree =
| Empty
| Node of 'a * 'a binary_search_tree * 'a binary_search_tree
let rec size tree =
match tree with
| Empty -> 0
| Node (_, left, right) -> size left + size right + 1
let rec insert data tree =
match tree with
| Empty -> Node (data, Empty, Empty)
| Node (v, left, right) ->
if data < v then
Node (v, insert data left, right)
else if data > v then
Node (v, left, insert data right)
else
tree
let rec contains data tree =
match tree with
| Empty -> false
| Node (v, left, right) ->
if data < v then
contains data left
else if data > v then
contains data right
else
true
let rec inorder tree =
match tree with
| Empty -> []
| Node (v, left, right) -> inorder left @ [v] @ inorder right
Exercises.hs
module Exercises
( change,
firstThenApply,
powers,
meaningfulLineCount,
Shape(..),
BST(Empty),
volume,
surfaceArea,
size,
contains,
insert,
inorder
) where
import qualified Data.Map as Map
import Data.List(isPrefixOf, find)
import Data.Char(isSpace)
change :: Integer -> Either String (Map.Map Integer Integer)
change amount
| amount < 0 = Left "amount cannot be negative"
| otherwise = Right $ changeHelper [25, 10, 5, 1] amount Map.empty
where
changeHelper [] remaining counts = counts
changeHelper (d:ds) remaining counts =
changeHelper ds newRemaining newCounts
where
(count, newRemaining) = remaining `divMod` d
newCounts = Map.insert d count counts
firstThenApply :: [a] -> (a -> Bool) -> (a -> b) -> Maybe b
firstThenApply xs p f = f <$> find p xs
powers :: Integral a => a -> [a]
powers base = map (base^) [0..]
meaningfulLineCount :: FilePath -> IO Int
meaningfulLineCount filePath = do
document <- readFile filePath
let allWiteSpace = all isSpace
trimStart = dropWhile isSpace
isMeaningful line =
not (allWiteSpace line) &&
not ("#" `isPrefixOf` (trimStart line))
return $ length $ filter isMeaningful $ lines document
data Shape
= Sphere Double
| Box Double Double Double
deriving (Eq, Show)
volume :: Shape -> Double
volume (Sphere r) = (4 / 3) * pi * (r ^ 3)
volume (Box w l d) = w * l * d
surfaceArea :: Shape -> Double
surfaceArea (Sphere r) = 4 * pi * (r ^ 2)
surfaceArea (Box w l d) = 2 * ((w * l) + (w * d) + (l * d))
data BST a = Empty | Node a (BST a) (BST a)
insert :: Ord a => a -> BST a -> BST a
insert x Empty = Node x Empty Empty
insert x (Node y left right)
| x < y = Node y (insert x left) right
| x > y = Node y left (insert x right)
| otherwise = Node y left right
contains :: Ord a => a -> BST a -> Bool
contains _ Empty = False
contains x (Node y left right)
| x < y = contains x left
| x > y = contains x right
| otherwise = True
size :: BST a -> Int
size Empty = 0
size (Node _ left right) = 1 + size left + size right
inorder :: BST a -> [a]
inorder Empty = []
inorder (Node x left right) = inorder left ++ [x] ++ inorder right
instance (Show a) => Show (BST a) where
show :: Show a => BST a -> String
show Empty = "()"
show (Node x Empty Empty) = "(" ++ show x ++ ")"
show (Node x Empty right) = "(" ++ show x ++ show right ++ ")"
show (Node x left Empty) = "(" ++ show left ++ show x ++ ")"
show (Node x left right) = "(" ++ show left ++ show x ++ show right ++ ")"
exercises.ts
import { open } from "node:fs/promises"
export function change(amount: bigint): Map<bigint, bigint> {
if (amount < 0) {
throw new RangeError("Amount cannot be negative")
}
let counts: Map<bigint, bigint> = new Map()
let remaining = amount
for (const denomination of [25n, 10n, 5n, 1n]) {
counts.set(denomination, remaining / denomination)
remaining %= denomination
}
return counts
}
export function firstThenApply<T, U>(
array: T[],
predicate: (x: T) => boolean,
consumer: (x: T) => U | undefined
): U | undefined {
const first = array.find(predicate)
if (first) {
return consumer(first)
}
return undefined
}
export function* powersGenerator(base: bigint): Generator<bigint> {
for (let power = 1n; true; power *= base) {
yield power
}
}
export async function meaningfulLineCount(filename: string): Promise<number> {
let count = 0
const file = await open(filename, "r")
for await (const line of file.readLines()) {
const trimmed = line.trim()
if (trimmed && !trimmed.startsWith("#")) {
count++
}
}
return count
}
interface Sphere {
kind: "Sphere"
readonly radius: number
}
interface Box {
kind: "Box"
readonly width: number
readonly length: number
readonly depth: number
}
export type Shape = Sphere | Box
export function volume(shape: Shape): number {
switch (shape.kind) {
case "Sphere":
return (4 / 3) * Math.PI * shape.radius ** 3
case "Box":
return shape.width * shape.length * shape.depth
}
}
export function surfaceArea(shape: Shape): number {
switch (shape.kind) {
case "Sphere":
return 4 * Math.PI * shape.radius ** 2
case "Box":
const { length, width, depth } = shape
return 2 * (length * width + length * depth + width * depth)
}
}
export interface BinarySearchTree<T> {
size(): number
insert(data: T): BinarySearchTree<T>
contains(data: T): boolean
inorder(): Iterable<T>
}
export class Empty<T> implements BinarySearchTree<T> {
size(): number {
return 0
}
insert(data: T): BinarySearchTree<T> {
return new Node<T>(data, new Empty<T>(), new Empty<T>())
}
contains(data: T): boolean {
return false
}
*inorder(): Iterable<T> {}
toString(): string {
return "()"
}
}
class Node<T> implements BinarySearchTree<T> {
constructor(
private readonly data: T,
private readonly left: BinarySearchTree<T>,
private readonly right: BinarySearchTree<T>
) {}
size(): number {
return this.left.size() + this.right.size() + 1
}
insert(data: T): BinarySearchTree<T> {
if (data < this.data) {
return new Node(this.data, this.left.insert(data), this.right)
} else if (data > this.data) {
return new Node(this.data, this.left, this.right.insert(data))
} else {
return this
}
}
contains(data: T): boolean {
if (data < this.data) {
return this.left.contains(data)
} else if (data > this.data) {
return this.right.contains(data)
} else {
return true
}
}
*inorder(): Iterable<T> {
yield* this.left.inorder()
yield this.data
yield* this.right.inorder()
}
toString(): string {
const left = this.left instanceof Empty ? "" : this.left.toString()
const right = this.right instanceof Empty ? "" : this.right.toString()
return `(${left}${this.data}${right})`
}
}