Last active
April 2, 2021 03:28
-
-
Save seisvelas/344468193115fed24973d8b109fdd017 to your computer and use it in GitHub Desktop.
Sieve of Eratosthenes: Lisp vs JavaScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { range } = require('range'); | |
const sieve = (field) => | |
// initialize | |
(typeof field === 'number') | |
? sieve(range(2, field)) | |
// recursive case | |
: (field.length) | |
? [field[0]].concat( | |
sieve(field.slice(1)) | |
.filter(e => e % field[0]) | |
) | |
// base case | |
: [] | |
sieve(10) // [2, 3, 5, 7] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#lang racket | |
(define (sieve field) | |
; initialize | |
(cond [(number? field) (sieve (range 2 field))] | |
; base case | |
[(empty? field) '()] | |
; recursive case | |
[else (let* [(prime (first field)) | |
(multiple? (λ (n) (not (zero? (modulo n prime)))))] | |
(cons prime (sieve (filter multiple? (rest field)))))])) | |
(sieve 10) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn sieve(limit: u32) -> Vec<u32> { | |
let mut candidates: Vec<u32> = (1..limit+1).collect(); | |
let mut i = 0; | |
while i < candidates.len() { | |
let mut j = i + 1; | |
while j < candidates.len() { | |
if candidates[j] % candidates[i] == 0 { | |
candidates.remove(j); | |
} | |
j += 1; | |
} | |
i += 1; | |
} | |
candidates | |
} | |
fn main() { | |
println!("{:?}", sieve(15)); // [1, 3, 5, 7, 11, 13] | |
} |
Khety (my daughter) is also working on this so while she tries it in Python, I tried it in Rust. So that's why a Rust version is pegged on here.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
My gf and I were playing with prime number schemes in the library (Vásconcelos, not like a code library lol) when I saw her unwittingly develop the Sieve of Eratosthenes. Then we made a functional version in JS and I noticed how Lispy it felt, so I tried it in a Lisp to compare.
Downsides of JS
switch
that were an expression instead of a statement, like Lisp'scond
)range(start, end, step)
function. Lots of hacks exist, I chose to use the npm range package. For some reason JS offers a fantastic variety of array methods but totally ignores other functional staples like range or even methods for other, non-array data types. Elixir provides an example of how much better this could be done.Upsides of JS
undefined
in JS. Plus, filter (as well asmap
,reduce
, and others are all methods of arrays. This makes programming with these methods terse AND more readable. I'm in the small minority of JS devs who think this is a good thing. A (likely superior) alternative is again provided by Elixir, which has separate functions for different numbers of possible arguments (eg to replicate JS's reduce, you'd have a reduce that takes one argument [just each element], a reduce with two arguments [for each element and its index], and finally a third that gives each element, its index, and a copy of the original array). To get similar functionality from Racket I see people generally use the ridiculously powerfulfor/list
.Advantages of Racket
Disadvantages of Racket
**
just alias topow
, for example? In our case, I dislike having to typemodulo
instead of just%
(which these days is a common enough idiom for modulo operations).#lang racket
should be the default when I use theracket
command/binary to run a file of unknown source code. What's weird is if I run Racket, it opens a Racket REPL, but when I run a Racket file, it suddenly becomes language agnostic. If there is no #lang line, Racket should be assumed.N.B. In both cases I abuse the weak typing for function arguments in order to be terse/lazy. I initially take a number for the
field
argument, but then take an array/list (for JS/Racket respectively). In reality, I only ever want an array or list. A more conventional solution is to make an initializer function which sets up the initial list, then passes it to a recursive function that takes care of the meat and potatoes of the algorithm. But since I'm just playing, I'm giving myself lots of slack. I say this in case someone reads this Gist in 5 years and is deciding to hire me or not.