The Big Book of Small EK9 Programs
Adapted from Al Sweigart's The Big Book of Small Python Projects: 81 Easy Practice Programs (No Starch Press, 2021; copyright © 2021 Al Sweigart), which he makes freely available under a Creative Commons Attribution-NonCommercial-ShareAlike licence at inventwithpython.com/bigbookpython. Changes were made: each program was ported from Python to EK9 and remodelled in EK9's idioms.
Project 64 — Seven-Segment Display Module
This is project 64 of the book, ported to EK9. Its chapter is still being written; until then, this page shows the port itself: a run of it, the whole program, and how it keeps to the original.
A sample run
Run it with:
ek9 sevensegment.ek9 1234567890 12
It prints:
__ __ __ __ __ __ __ __ __ __
| | | | | __| __| |__| |__ |__ | |__| |__| | |
|__| |__| | |__ __| | __| |__| | |__| __| |__|
The program
#!ek9 defines module bigbook.sevensegment defines type List of Shape defines record //One numeral drawn as three rows, top to bottom. A record keeps a numeral's rows together, //where three separate tables keyed by the same numerals could drift apart. Shape top as String: String() middle as String: String() bottom as String: String() default Shape() as pure Shape() as pure -> topRow as String middleRow as String bottomRow as String top :=? String(topRow) middle :=? String(middleRow) bottom :=? String(bottomRow) default operator ? defines class //THE OUTPUT: everything the reader sees goes through here. The display has no words, so //there is no Say, and the number is a program argument, so there is no LineInput. Console stdout as Stdout: Stdout() //The numerals are computed: they are shown as the game drew them, a row at a time. showRows() -> rows as List of String for row in rows stdout.println(row) override operator ? as pure <- rtn as Boolean: true defines function //THE GAME: the numerals, as rows, with no output. //The book's table of shapes: data, every shape four characters wide. An Integer's text //holds only digits and a minus sign. numeralShapes() as pure <- rtn as Dict of (Character, Shape): { '0': Shape(" __ ", "| |", "|__|"), '1': Shape(" ", " |", " |"), '2': Shape(" __ ", " __|", "|__ "), '3': Shape(" __ ", " __|", " __|"), '4': Shape(" ", "|__|", " |"), '5': Shape(" __ ", "|__ ", " __|"), '6': Shape(" __ ", "|__ ", "|__|"), '7': Shape(" __ ", " |", " |"), '8': Shape(" __ ", "|__|", "|__|"), '9': Shape(" __ ", "|__|", " __|"), '-': Shape(" ", " __ ", " ") } shapeOf() as pure -> numeral as Character <- rtn as Shape: Shape() shapes <- numeralShapes() rtn: shapes.getOrDefault(numeral, Shape()) topOf() as pure -> shape as Shape <- rtn as String: shape.top middleOf() as pure -> shape as Shape <- rtn as String: shape.middle bottomOf() as pure -> shape as Shape <- rtn as String: shape.bottom //The book puts one space between neighbouring numerals. spaced() as pure -> left as String right as String <- rtn as String: `${left} ${right}` //The book's getSevSegStr: the number zero-padded to at least minimumWidth digits, drawn as //three rows. The book joins the rows with newlines; a List of rows leaves that to the caller. sevenSegmentRows() as pure -> number as Integer minimumWidth as Integer <- rtn as List of String: List() of String digits <- $number shapes <- cat digits.zeroPad(minimumWidth) | map with shapeOf | collect as List of Shape topRow <- cat shapes | map with topOf | join with spaced | collect as String middleRow <- cat shapes | map with middleOf | join with spaced | collect as String bottomRow <- cat shapes | map with bottomOf | join with spaced | collect as String rtn: [topRow, middleRow, bottomRow] defines program //Sevseg (Big Book project 64). Draws the number it is given as seven-segment display digits. //The game decides; the Console shows it. SevenSegment() -> number as Integer minimumWidth as Integer console <- Console() console.showRows(sevenSegmentRows(number, minimumWidth))
About this port
- Faithful to the book:
sevenSegmentRows(number, minimumWidth)is the book'sgetSevSegStr(number, minWidth): the number zero-padded to at leastminimumWidthdigits (String.zeroPad, which keeps a minus sign in front as Python'szfilldoes), each numeral drawn four characters wide, and ONE space between neighbouring numerals. Countdown and Digital Clock call it with a width of 2. - Deliberate divergences: the book returns one string joined with newlines; the port returns a
List of String, one row each, and the program prints them. Run on its own, the book prints a fixed usage message; this port draws the number it is given..is not drawn, because an Integer has no decimal point. - Model: each numeral is a
Shaperecord (top, middle, bottom row) in oneDictliteral keyed byCharacter, instead of three tables that must agree. The table is data, returned by a purenumeralShapes();shapeOfonly looks a numeral up in it. Each row is a stream:map with topOf | join with spaced. Nothing is accumulated, so every function isas pure.