Skip to content

Instantly share code, notes, and snippets.

@jcmkk3
Last active October 6, 2023 17:22
Show Gist options
  • Save jcmkk3/93ccae040baa1b2960ffce5441f0c1ef to your computer and use it in GitHub Desktop.
Save jcmkk3/93ccae040baa1b2960ffce5441f0c1ef to your computer and use it in GitHub Desktop.
Simple Average in Many Languages

Simple Average in Many Languages

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.).

Python

# 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

Ruby

# 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

JavaScript

// 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
}

Lua

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

Go

func average(numbers []int) float64 {
	var sum int
	var count int

	for _, number := range numbers {
		sum += number
		count++
	}

	return float64(sum) / float64(count)
}

Swift

// 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)
}

Rust

// 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
}

Zig

// 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);
}

C

// 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;
}

Scheme

; 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)))

Clojure

; 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 /)))

Elixir

# 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

Grain

// 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
}

Miranda

average numbers = average' numbers 0 0
average' [] sum count = sum / count
average' (number:numbers) sum count = average numbers (sum + number) (count + 1)

Wipple

-- 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
}

APL

 Verbose solution
average  {(+) ÷ }


 Tacit solution
average  +÷

BQN

# Verbose solution
Average  {(+´𝕩) ÷ 𝕩}


# Tacit solution
Average  +´÷

Lil

# 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

Q or K

// Standard library
avg


// Verbose solution
average: {[numbers] sum[numbers] % count numbers}


// Compact solution
average: {(+/x) % #x}

R

# 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
}

Julia

# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment