Let's compare ruby, lisp and python

Several algorithms in three programming languages

Let's take a few simple tasks and see how the supposedly dead lisp and modern python and ruby ​​cope with them. We will compare the speed of operation, as well as the compactness and readability of the code. We test on a computer with an Intel Core i3 2.93 GHz processor and 14 GB memory. We use interpreters Lisp SBCL 2.3.2, Python 3.12.4, Ruby 3.3.3. The author immediately wants to note: he did not try to invent or find the most efficient algorithms; The goal was precisely to compare the performance of identical algorithms in different languages. This is what happened.

Real numbers

Calculate the distance between two points A(3,7) and B(-1,5) on the plane.

As you know, the distance between two points A(xa,ya) And B(xb,yb) calculated by the formula

\sqrt{(x_a-x_b)^2+(y_a-y_b)^2}

Lisp:

;; distance.lisp
;; расстояние между двумя точками на плоскости

; для улучшения читаемости переопределим функцию возведения в степень expt
(defmacro ^ (val power)  
  `(expt ,val ,power))

(defun distance ()  
  (let ((a (list :x 3 :y 7))         
        (b (list :x -1 :y 5)))    
       (sqrt (+ (^ (- (getf b :x) (getf a :x)) 2)                 
                (^ (- (getf b :y) (getf a :y)) 2)))))

Point coordinates A And B are presented in the form of lists with keys, which makes it convenient to extract them from lists using the function getf. Next, the variables a and b are assigned values ​​and the distance is calculated.

Result: 4.472136

Python:

# distance.py
# расстояние между двумя точками на плоскости
import math
def distance():    
  a = {'x': 3, 'y': 7}    
  b = {'x': -1, 'y': 5}    
  return math.sqrt((b['x'] - a['x'])**2 + (b['y'] - a['y'])**2)
print (distance())

Result: 4.47213595499958

The first line of the program imports the math library to make the square root function available. sqrt. Point coordinates A And B – hashes with symbolic keys 'x' And 'y'which allows you to retrieve values ​​based on these keys.

Ruby:

# distance.rb
# расстояние между двумя точками на плоскости
def distance  
  a, b = {x: 3, y: 7}, {x: -1, y: 5}  
  Math.sqrt((b[:x] - a[:x])**2 + (b[:y] - a[:y])**2)
end
puts distance

Result: 4.47213595499958
For coordinates, hashes with symbolic keys are used. In the first line of the function, the variables a And b values ​​are assigned, in the second the distance is calculated.

In the author's opinion, after applying the macro, the most readable code was in Lisp. Lisp's output accuracy is less, but this is only output. The internal representation of the number provides sufficient accuracy.

Large integers

Let's write a program that prints the sum of the first n terms of a geometric progression starting with one with denominator q (2).
The solution is based on the use of the well-known formula for the sum of a geometric progression:

S = \frac{q^n - 1}{q - 1}

At q = 2 And n = 64 we get the famous “chess” number 18446744073709551615.

Lisp:

(defun sum-g (n q)  
  (/ (- (expt q n) 1) (- q 1)))

Python:

def sum_g (n, q):    
  return (q**n - 1) // (q -1)
print (sum_g (64, 2))

Ruby:

def sum_g (n, q)  
  (q**n - 1) / (q -1)
end
print (sum_g 64, 2)

All three systems work with large integers. The most compact and expressive code is from old man Lisp.

Large massifs

Let's create an array of prime numbers using Eratosthenes' algorithm for n = 10 million numbers. The algorithm (“Sieve of Eratosthenes”) is as follows: we form an array with numbers from 2 to n. Then we remove from this array all numbers that are first multiples of the first number of the array, except the number itself, then the second, etc. until the end of the array. This way, only numbers that are multiples of themselves and one will remain in the array. The algorithm is partially taken from (2).

Lisp:

; создаем массив a с числами от 2 до n
(setf a (make-array (1+ n)))
(setf (aref a 0) nil)
(setf (aref a 1) nil)
(loop for i from 2 to n do  
      (setf (aref a i) i))  
; удаляем из него все составные числа  
(do ((i 2 (1+ i)))
  (( > i (floor (sqrt n))))    
  (when (not (aref a i))      
    (go end))    
  (setf j (* i i))    
  (loop  while (<= j n) do      
         (setf (aref a j) nil)      
         (setf j (+ i j)))    
  end)  
(setf a (remove nil a))
a)

In Lisp in a loop do There are some possibilities missing, so to continue the loop you have to apply a jump to the label.

Operating time – 6 sec

Python:

import array, math
def primes (n):    
  a = [None, None]    
  for i in range(2,n+1):        
    a.append(i)     
  for i in range(2, int(math.sqrt(n))+1):        
    if not a[i]:            
      continue        
    j = i*i        
    while j <= n:                
      a[j] = None            
      j += i           
  a = [i for i in a if i]    # удалим None    
  print (a)
primes (10000000)

Operating time – 7 sec

Ruby:

n = Integer(ARGV[0])
sieve = [nil, nil]
(2..n).each {|i| sieve << i}
for i in 2 .. Math.sqrt(n)
  next if !sieve[i]
  (i*i).step(n,i) { |j| sieve[j]=nil}
end
p sieve.compact  

Operating time: 5 sec.

The performance of the systems is approximately the same, but in terms of expressiveness and compactness of code, Ruby is far ahead. We can say that the Ruby code here looks simply elegant.

Recursion

We will calculate the fortieth Fibonacci number using the formula:
F(0) = 0, f(1) = 1, f(n) = f(n-1) + f(n-2)

Lisp:

(defun fib (n)
  (cond ((< n 2) n)
            (t(+ (fib (- n 1)) (fib (- n 2)))))) 

Calculation time 5 sec

Python:

def fib (n):
  return n if n < 2 else fib(n-1) + fib(n-2) 

Calculation time 39 sec.

Ruby:

def fib(n)
n < 2? n : fib(n-1) + fib(n-2)
end

def fib (n)  
  n < 2? n : fib(n-1) + fib(n-2)
end

Time 18 sec.

In this recursive algorithm, Lisp was the fastest; Ruby is mediocre, and Python is hopelessly behind. The code is equally compact and readable on all three systems.

Summarizing our small study, we can say that all three systems have a right to exist, but in the author’s opinion, Ruby is slightly ahead of its rivals in such parameters as compactness, code readability, and speed. Lisp is practically not far behind, while Python is in honorable third place.

Literature:

  1. Simdyanov I.V. Ruby tutorial. — St. Petersburg: BHV-Petersburg, 2020

  2. E. A. Roganov, N. A. Roganova Programming in the Ruby language. – MGIU, Moscow, 2008

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *