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 26 — Fibonacci
This is project 26 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
The player's answers are piped in:
10
100
QUIT
Run it with:
ek9 fibonacci.ek9 < input.txt
It prints:
Fibonacci Sequence, by Al Sweigart al@inventwithpython.com
The Fibonacci sequence begins with 0 and 1, and the next number is the
sum of the previous two numbers. The sequence continues forever:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987...
Enter the Nth Fibonacci number you wish to
calculate (such as 5, 50, 1000, 9999), or QUIT to quit:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34
The #10 Fibonacci number is 34
Enter the Nth Fibonacci number you wish to
calculate (such as 5, 50, 1000, 9999), or QUIT to quit:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738, 19740274219868223167, 31940434634990099905, 51680708854858323072, 83621143489848422977, 135301852344706746049, 218922995834555169026
The #100 Fibonacci number is 218922995834555169026
Enter the Nth Fibonacci number you wish to
calculate (such as 5, 50, 1000, 9999), or QUIT to quit:
Thanks for playing!
The program
#!ek9 defines module bigbook.fibonacci defines constant //The sequence starts from two given terms, 0 and 1. The book prints the answer for those //two from fixed text ending in a full stop; every calculated answer has none. GivenTerms <- 2 defines type List of BigInteger defines class //THE input idiom of this book, copied unchanged from lib/LineInput.ek9 (the reasons are //there): read with next(), and every input loop also stops when ended() is true. LineInput stdin as Stdin: Stdin() inputEnded as Boolean: false next() <- rtn as String: stdin.next() if not rtn? inputEnded: true ended() as pure <- rtn as Boolean: Boolean(inputEnded) override operator ? as pure <- rtn as Boolean: true //THE INPUT AND OUTPUT: everything the player sees or types goes through here, worded by Say. Console stdout as Stdout: Stdout() input as LineInput: LineInput() say as Say: Say("en") Console() -> words as Say say: words showIntroduction() stdout.println(say.introduction()) //The term the player asks for; unset when they QUIT or the input ends. nextTerm() <- rtn as Integer: Integer() quitting <- false while not rtn? and not quitting and not input.ended() stdout.println(say.askTerm()) if entry <- input.next() response <- entry.trim().upperCase() if response == say.quit() quitting: true else rtn: termWanted(response) if not rtn? stdout.println(say.notATerm()) //Printed one term at a time: 9999 terms make a line of megabytes, too long to build up //as a single String first. showSeries() -> terms as List of BigInteger stdout.println("") separator <- "" for term in terms stdout.print(`${separator}${term}`) separator: ", " stdout.println("") stdout.println("") showAnswer() -> nth as Integer answer as BigInteger stdout.println(isGivenTerm(nth) <- say.givenTerm(nth, answer) : say.calculatedTerm(nth, answer)) showGoodbye() stdout.println(say.goodbye()) ended() as pure <- rtn as Boolean: input.ended() override operator ? as pure <- rtn as Boolean: true defines function //THE GAME: the sequence, with no input or output. //A term number above 0, or unset for anything else. termWanted() as pure -> entry as String <- rtn as Integer: Integer() if term <- Integer(entry) then term > 0 rtn: term //The terms the sequence starts from, rather than calculates. isGivenTerm() as pure -> nth as Integer <- rtn as Boolean: nth <= GivenTerms //The first `count` Fibonacci numbers. BigInteger, because the 94th no longer fits in the //64 bits of an Integer, and an Integer that overflows becomes unset. //`+` makes a new number, so moving the names along never changes a term already stored. fibonacciSeries() -> count as Integer <- rtn as List of BigInteger: List() of BigInteger previous <- BigInteger(0) current <- BigInteger(1) while length rtn < count rtn += previous following <- previous + current previous: current current: following defines function //THE language of the words, copied unchanged from lib/Language.ek9: BIGBOOK_LANG when it //is set (fr, zh, ...), otherwise en. language() <- rtn as String: EnvVars().get("BIGBOOK_LANG") ?: "en" //THE WORDS: every word the player sees. defines text for "en" Say introduction() `Fibonacci Sequence, by Al Sweigart al@inventwithpython.com The Fibonacci sequence begins with 0 and 1, and the next number is the sum of the previous two numbers. The sequence continues forever: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987... ` askTerm() `Enter the Nth Fibonacci number you wish to calculate (such as 5, 50, 1000, 9999), or QUIT to quit:` quit() "QUIT" notATerm() "Please enter a number greater than 0, or QUIT." givenTerm() -> nth as Integer answer as BigInteger `The #${nth} Fibonacci number is ${answer}.` calculatedTerm() -> nth as Integer answer as BigInteger `The #${nth} Fibonacci number is ${answer}` goodbye() "Thanks for playing!" defines program //Fibonacci Sequence (Big Book project 26). Prints the first N terms of the sequence and the //Nth, for each N typed, until QUIT. The game decides; the Console asks and tells, in the //player's words. Fibonacci() //A language with no words of its own is unset: ?: falls back to English. console <- Console(Say(language()) ?: Say("en")) console.showIntroduction() running <- true while running and not console.ended() if nth <- console.nextTerm() terms <- fibonacciSeries(nth) console.showSeries(terms) console.showAnswer(nth, terms.getOrDefault(nth - 1, BigInteger())) else running: false //The player quit: the game says goodbye. At the end of the input it does not. console.showGoodbye()
About this port
- Faithful to the book: the intro; ask for N (QUIT ends with "Thanks for playing!"; anything
that is not a number above 0 is refused); print the first N terms and "The #N Fibonacci number
is X". The book prints N=1 and N=2 from fixed text that ends in a full stop and its calculated
answers without one; the port keeps that difference (
GivenTerms) without special-casing the calculation. - Omitted: the "this will take a while, press Enter" pause the book shows for N >= 10000.
- Idioms:
- The terms are
BigInteger. A 64-bitIntegerholds the sequence only up to the 93rd term; from the 94th the sum overflows and anIntegerbecomes unset, so the port printed no answer at all for N=100, 1000 or 9999, the values its own prompt suggests. fibonacciSerieskeeps numbers as numbers and stops onlength rtn < count, so it needs no counter.+returns a newBigInteger, so movingpreviousandcurrentalong never alters a term already in the list.- The terms are a computed table, not text:
showSeriesprints them one at a time rather than joined into oneString, because N=9999 makes a line of about 10 MB, and joining it pairwise would copy that text thousands of times. nextTermreturns an unsetIntegerfor QUIT or the end of input, and the program's loop guards on it. The number check is a guard with a condition:if term <- Integer(entry) then term > 0.
- The terms are