Dość często jak mówię o Elixir czy też piszę pojawia się pytanie: a dlaczego nie F#? Dlaczego nie lisp? Clojure? Haskell? Odpowiedź jest dość prosta, ale może ona nie być zrozumiała dla niektórych. Elixir jest po prostu czytelny, beznadziejnie prosty i przejrzysty.
Poniżej, ta sama funkcja napisana w kilku językach funkcyjnych jak i obiektowych, teraz, proszę powiedzcie mi, która forma jest czytelna i jak w tym wszystkim plasuje się elixir?
;; lisp (defun fibonacci (n &optional (a 0) (b 1) (acc ())) (if (zerop n) (nreverse acc) (fibonacci (1- n) b (+ a b) (cons a acc)))) ;; clojure (def fib-seq ((fn rfib [a b] (lazy-seq (cons a (rfib b (+ a b))))) 0 1)) -- haskell fib :: Integer -> Integer fib 0 = 1 fib 1 = 1 fib n = fib (n-1) + fib (n-2) % erlang -module(fib). -export([fib/1]). fib(1) -> 1; fib(2) -> 1; fib(N) -> fib(N - 2) + fib(N - 1). // f# let rec fib n = match n with | 1 | 2 -> 1 | n -> fib(n-1) + fib(n-2) (* OCaml *) let rec fib = function | 0 -> 0 | 1 -> 1 | n -> fib (n-1) + fib (n-2) // Java public static long fib(int n) { if (n <= 1) return n; else return fib(n-1) + fib(n-2); } // C# public int Fib(int n) { if (n == 0 || n == 1) { return n; } else { return Fib(n - 1) + Fib(n - 2); } }
A teraz to samo w elixir:
# elixir defmodule Fib do def fib(0) do 0 end def fib(1) do 1 end def fib(n) do fib(n-1) + fib(n-2) end end
Jak widać nie różni się to zbytnio od zapisu C#. Do tego jest w tym wypadku przejrzysty jak haskell czy też erlang. Jednak te dwa języki szybko potrafią się stać mało czytelne. Czyli wiedziałem, że raczej z poziomu samego języka nie będe miał problemu zarówno zrozumieniem co jest napisane jak i w ogóle nauczeniem się jego posługiwaniem.
Rozumiem, że Fibonacci w Elixirze wygląda ładnie, aczkolwiek przyczepiłbym się do Clojure :-) Najprostsze Fibonacci bez TCO, lazy streamów i innych cudów wyglądałoby następująco https://repl.it/IXHx (link do repla). Tak więc jedynym udziwnieniem jest nienaturalna dla większości programistów notacja lispowa.
Proszę o potraktowanie mojej wiadomości jako ciekawostki, nie chcę wywoływać świętej wojny. Niech każdy programuje w czym lubi i wybiera język odpowiedni do potrzeb :-)
@Paweł – i tylko te 5 ( albo i 6) nawiasów na końcu ;-)
IMHO notacje F# i OCaml tak bardzo nie odbiegają od eliksirowej. Pomijam oczywiście – moim zdaniem przeklętą – wrażliwość na wcięcia… Coś okropnego…
A z innej beczki – na ile to wszystko rozumiem – to te implementacje nie są tożsame? Zdaje się cześć obsługuje element zero a część nie…
Oj tak, “))))))))))))))))))))))))” i wysoki próg wejścia sprawiły, że zniechęciłem się do nauki Clojure.
Oo dziękuje ale i tak odwrotna notacja Polska i do tego te nawiasy ;)
W C# brakuje słowa kluczowego ‘return’.
A w elixir by zadziałało! :)
Cześć
ciąg Fibonacciego można napisać pozbywając się podwójnego wywołania. Czytelnie i szybko. C#
public long Fibonacci(long parameterA, long parameterB, long n)
{
if (n == 1)
return parameterB;
return Fibonacci(parameterB, parameterA + parameterB, n – 1);
}
Tu cały kod porównujący ilość wywołań i czasy obu metod(mam nadzieję, że działa)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Diagnostics;
namespace FibTest
{
public class Program
{
public static long callBadFibonacciCount;
public static long callGoodFibonacciCount;
public static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
long goodResult = FibonacciGood(0, 1, 10);
stopwatch.Stop();
Console.WriteLine(“good result: {0}, calls: {1}, time: {2}”, goodResult, callGoodFibonacciCount, stopwatch.Elapsed);
stopwatch.Restart();
long badResult = FibonacciBad(10);
stopwatch.Stop();
Console.WriteLine(“bad result: {0}, calls: {1}, time: {2}”, badResult, callBadFibonacciCount, stopwatch.Elapsed);
}
public static long FibonacciGood(long parameterA, long parameterB, long n)
{
callGoodFibonacciCount++;
if (n == 1)
return parameterB;
return FibonacciGood(parameterB, parameterA + parameterB, n – 1);
}
public static long FibonacciBad(long n)
{
callBadFibonacciCount++;
if (n == 1)
return 1;
if (n == 0)
return 0;
return FibonacciBad(n – 1) + FibonacciBad(n – 2);
}
}
}
Pozdrawiam serdecznie
czesc, dzieki. tak, mozna tez tak zrobic, co w jezykach funkcyjnych spowodowaloby tail optimization i przy duzych liczbach fib dzialaloby super fajnie. Jednak tutaj warto to zaznaczyc, w kazdym z jezykow ktore wymenilem da sie to zrobic w inny sposob do tego stopnia, ze w niektorych da sie cachowac poprzedni wyniki wiec nigdy nie liczy sie juz raz wykonanego dzialania.
wiec jak najbardziej da sie tak zapisać i będzie on dużo mniej rzeczy na stos odkładał :) jednak jakbsmy juz tak bardzo chcieli w C# by to bylo wydajne i szybkie, to while/for bylby tutaj najlepszy.
[…] Jednym z pierwszych problemów jak i największą zaletą Elixir jest sama semantyka języka. Jest ona prosta, banalna, ograniczona do kilku makr. Opanowanie językowej składni to maks kilka minut nauki. Opanowanie możliwości czytania kodu ze zrozumieniem – około dwóch tygodni maks. Język nie wygląda jak język funkcyjny. Wręcz przeciwnie, wygląda jak język obiektowy. O czym ostatnio pisałem. […]
Comments are closed.