This was an exercise to take a peek at some different languages using a simple algorithm.
Average is an interesting algorithm to compare languanges. It is simple, but has enough complexity to demonstrate some differences between the languages.
Average requires calculating two different summary statistics (sum and count), then combining those two (division) into a single number. An efficient solution would only traverse the collection of numbers once.
Each of these are written as a function that would be given a collection of numbers. Whatever a collection means in that particular language. Some languages have multiple solutions that might have different tradeoffs.
Only the happy path is handled. Cases like having an empty collection or non-numeric elements, etc. are not handled.
Most solutions use the same names (average, numbers, number, sum, count) in order to make comparisons easier, even if their might be more idiomatic names for that language.
Languages are roughly grouped with other similar language families (object oriented, imperative, lisps, functional, array oriented, etc.).
# Standard library
import statistics
statistics.mean
# Compact solution
def average(numbers):
return sum(numbers) / len(numbers)
# Imperative solution
def average(numbers):
sum = 0
count = 0
for number in numbers:
sum += number
count += 1
return sum / count
# Compact solution
def average(numbers)
numbers.sum / numbers.size
end
# Imperative solution
def average(numbers)
sum = 0
count = 0
numbers.each do |number|
sum += number
count += 1
end
sum / count
end
// Compact solution
let average = (numbers) => numbers.reduce((a, b) => a + b) / numbers.length
// Imperative solution
function average(numbers) {
let sum = 0
let count = 0
for (number of numbers) {
sum += number
count++
}
return sum / count
}
function average(numbers)
local sum = 0
local count = 0
for _, number in ipairs(numbers) do
sum = sum + number
count = count + 1
end
return sum / count
end
func average(numbers []int) float64 {
var sum int
var count int
for _, number := range numbers {
sum += number
count++
}
return float64(sum) / float64(count)
}
// Compact solution
func average(_ numbers: [Int]) -> Float {
return Float(numbers.reduce(.zero, +)) / Float(numbers.count)
}
// Imperative solution
func average(_ numbers: [Int]) -> Float {
var sum = 0
var count = 0
for number in numbers {
sum += number
count += 1
}
return Float(sum) / Float(count)
}
// Functional solution
func average(_ numbers: [Int]) -> Float {
let (sum, count) = numbers.reduce(
(0, 0), { acc, number in (acc.0 + number, acc.1 + 1) }
)
return Float(sum) / Float(count)
}
// Compact solution
fn average(numbers: &Vec<i32>) -> f64 {
let sum: i32 = numbers.iter().sum();
let count: usize = numbers.len();
sum as f64 / count as f64
}
// Imperative solution
fn average(numbers: &Vec<i32>) -> f64 {
let mut sum = 0;
let mut count = 0;
for number in numbers {
sum += number;
count += 1;
}
sum as f64 / count as f64
}
// Functional solution
fn average(numbers: &Vec<i32>) -> f64 {
let (sum, count) = numbers
.iter()
.fold((0, 0), |(sum, count), number| (sum + number, count + 1));
sum as f64 / count as f64
}
// Imperative solution
fn average(numbers: []const i32) f64 {
var sum: i32 = 0;
var count: i32 = 0;
for (numbers) |number| {
sum += number;
count += 1;
}
return @intToFloat(f64, sum) / @intToFloat(f64, count);
}
// Imperative solution
float average(int *numbers, size_t size) {
int sum = 0;
int i;
for (i = 0; i < size; i++) {
sum += numbers[i];
}
return (float)sum / (float)size;
}
; Recursive solution
(define (average numbers)
(let loop ((numbers numbers)
(sum 0)
(count 0))
(if (null? numbers)
(/ sum count)
(loop (cdr numbers)
(+ sum (car numbers))
(+ count 1)))))
; Functional solution
(define (average numbers)
(apply /
(fold
(lambda (number accumulator)
(list (+ number (car accumulator))
(+ 1 (cadr accumulator))))
'(0 0)
numbers)))
; Compact solution
(defn average [numbers]
(/ (reduce + numbers) (count numbers)))
; Recursive solution
(defn average [numbers]
(loop [numbers numbers
sum 0
count 0]
(if (empty? numbers)
(/ sum count)
(recur (rest numbers)
(+ sum (first numbers))
(inc count)))))
; Functional solution
(defn average [numbers]
(->> numbers
(reduce
(fn [[sum count] number]
[(+ sum number) (inc count)])
[0 0])
(apply /)))
# Compact solution
average = fn numbers -> Enum.sum(numbers) / Enum.count(numbers) end
# Recursive solution
defmodule Math do
def average(numbers) do
average(numbers, 0, 0)
end
defp average([], sum, count) do
sum / count
end
defp average([number | numbers], sum, count) do
average(numbers, sum + number, count + 1)
end
end
# Functional solution
average = fn numbers ->
[sum, count] =
Enum.reduce(numbers, [0, 0], fn number, [sum, count] ->
[sum + number, count + 1]
end)
sum / count
end
// Imperative solution
let average = (numbers) => {
let mut sum = 0
let mut count = 0
List.forEach(
(number) => {
sum += number
count += 1
},
numbers
)
sum / count
}
// Recursive solution
let rec average = (numbers, sum, count) => {
match (numbers) {
[] => sum / count,
[number, ...numbers] => average(numbers, sum + number, count + 1)
}
}
let average = (numbers) => average(numbers, 0, 0)
// Functional solution
let average = (numbers) => {
let (sum, count) = List.reduce(
((sum, count), number) => (sum + number, count + 1),
(0, 0),
numbers
)
sum / count
}
average numbers = average' numbers 0 0
average' [] sum count = sum / count
average' (number:numbers) sum count = average numbers (sum + number) (count + 1)
-- Compact solution
average : numbers -> sum numbers / count numbers as Number
-- Imperative solution
average : numbers -> {
sum : mutable 0
count : mutable 0
numbers . each (number -> {
sum . add! number
count . increment!
})
get sum / get count as Number
}
-- Functional solution
average : numbers -> {
sum , count : numbers . reduce (0 , 0) (number (sum , count) -> {
sum + number , count + 1
})
sum / count
}
⍝ Verbose solution
average ← {(+⌿⍵) ÷ ≢⍵}
⍝ Tacit solution
average ← +⌿÷≢
# Verbose solution
Average ← {(+´𝕩) ÷ ≠𝕩}
# Tacit solution
Average ← +´÷≠
# Compact solution
on average numbers do
sum numbers / count numbers
end
# Imperative solution
on average numbers do
# `sum` and `count` are reserved words so we append a `_`
sum_: 0
count_: 0
each number in numbers
sum_: sum_ + number
count_: count_ + 1
end
sum_ / count_
end
# Recursive solution
on average numbers _sum _count do
# `_sum` and `_count` are not meant to be passed in by the caller
# they are only used internally by the function and default to 0
if numbers ~ ()
_sum / _count
else
average[1 drop numbers
_sum + first numbers
_count + 1]
end
end
// Standard library
avg
// Verbose solution
average: {[numbers] sum[numbers] % count numbers}
// Compact solution
average: {(+/x) % #x}
# Standard library
mean
# Compact solution
average <- \(numbers) sum(numbers) / length(numbers)
# Imperative solution
average <- function(numbers) {
sum <- 0
count <- 0
for (number in numbers) {
sum <- sum + number
count <- count + 1
}
sum / count
}
# Standard library
using Statistics
mean
# Compact solution
average(numbers) = sum(numbers) / length(numbers)
# Imperative solution
function average(numbers)
sum = 0
count = 0
for number = numbers
sum += number
count += 1
end
sum / count
end