Category Archives: Python

Coconut – Istruzioni – 11

no-fun-allowed

Non so se si userà Coconut ma rockz😀 continuo da qui a copiare qui.

Istruzioni

Coconut supports significantly enhanced destructuring assignment, similar to Python’s tuple/list destructuring, but much more powerful. The syntax for Coconut’s destructuring assignment is

[match] <pattern> = <value> 

where <value> is any expression and <pattern> is defined by Coconut’s match statement. The match keyword at the beginning is optional, but is sometimes necessary to disambiguate destructuring assignment from normal assignment, which will always take precedence. Coconut’s destructuring assignment is equivalent to a match statement that follows the syntax:

match <pattern> in <value>:
    pass
else:
    err = MatchError(<error message>)
    err.pattern = "<pattern>"
    err.value = <value>
    raise err

If a destructuring assignment statement fails, then instead of continuing on as if a match block had failed, a MatchError object will be raised describing the failure.

c46

With Python can’t be done without a long series of checks in place of the destructuring assignment statement. See the compiled code for the Python syntax.

Decoratori
Unlike Python, which only supports a single variable or function call in a decorator, Coconut supports any expression.

L’esempio riportato non è completo e non ho trovato qualcosa di diverso da uno che seguirà prossimamente. Per cui riporto solo il codice senza eseguirlo.

Coconut:

@wrapper1 .. wrapper2 $(arg)
def func(x) = x**2

Python:

def wrapper(func):
    return wrapper1(wrapper2(arg, func))
@wrapper
def func(x):
    return x**2

Istruzioni else
Coconut supports the compound statements try, if, and match on the end of an else statement like any simple statement would be. This is most useful for mixing match and if statements together, but also allows for compound try statements.

Coconut:

try:
    unsafe_1()
except MyError:
    handle_1()
else: try:
    unsafe_2()
except MyError:
    handle_2()

Python:

try:
    unsafe_1()
except MyError:
    handle_1()
else:
    try:
        unsafe_2()
    except MyError:
        handle_2()

Istruzioni except
Python 3 requires that if multiple exceptions are to be caught, they must be placed inside of parentheses, so as to disallow Python 2’s use of a comma instead of as. Coconut allows commas in except statements to translate to catching multiple exceptions without the need for parentheses.

Coconut:

try:
    unsafe_func(arg)
except SyntaxError, ValueError as err:
    handle(err)

Python:

try:
    unsafe_func(arg)
except (SyntaxError, ValueError) as err:
    handle(err)


Lista di variabili

Coconut allows for the more elegant parenthetical continuation instead of the less elegant backslash continuation in import, del, global, and nonlocal statements.

c47

Codice passthrough
No, non so tradurlo (codice attraversante? orrendo!).
Coconut supports the ability to pass arbitrary code through the compiler without being touched, for compatibility with other variants of Python, such as Cython or Mython. Anything placed between \( and the corresponding close parenthesis will be passed through, as well as any line starting with \\, which will have the additional effect of allowing indentation under it.

Coconut:

\\cdef f(x):
    return x |> g

Python:

cdef f(x):
    return g(x)

:mrgreen:

Fare una REPL

phone-humour

L’altro giorno con GG si ragionava che certe operazioni ripetitive sarebbero semplificate usando una REPL, come quelle che uso di solito per Racket, Octave, Coconut, … E anche Python usato interattivamente anche se non si usa chiamarlo REPL è esattamente quella. Ma tra il dire e il fare…

La versione elementare è qualcosa come questa:

while read -p "> " st ; do
    echo $st
    if [[ $st == "Q" ]]; then
        exit 0
    fi
done

r0

Ma…

r1

non funzionano le frecce e i comandi di editing usuali (quelli che i giovani non conoscono, questi qui).

Aggiornamento: per l’edit della linea e l’uso delle frecce vedi il man di read e lo script with, qui.

La stessa cosa capita con Python:

#!/usr/bin/python3

while True:
    st = input('> ')
    print(st)
    if st.strip()[0] == 'Q': exit(0)

r2

Uhmmm… è un compito più impegnativo. Ma se provassimo a googlare?

Sì sorprese in quantità😀 come al solito spesso c’è qualche nerd da qualche parte che ci ha già pensato –e forse risolto😀
Ecco qualche esempio.

Ottimo Derick Bailey: Build Your Own App Specific REPL For Your NodeJS App con soluzioni per Ruby e NodeJS. Solo che andrebbe personalizzato e non sono i linguaggi preferiti (almeno non da entrambi).
Ancora più bella la soluzione di nodejitsu: How to create and use a custom REPL.

#!/usr/bin/node

var net = require("net"),
    repl = require("repl");

var mood = function () {
    var m = [ "^__^", "-___-;", ">.<", "" ];
    return m[Math.floor(Math.random()*m.length)];
};

//A remote node repl that you can telnet to!
net.createServer(function (socket) {
  var remote = repl.start("node::remote> ", socket);
  //Adding "mood" and "bonus" to the remote REPL's context.
  remote.context.mood = mood;
  remote.context.bonus = "UNLOCKED";
}).listen(5001);

console.log("Remote REPL started on port 5001.");

//A "local" node repl with a custom prompt
var local = repl.start("node::local> ");

// Exposing the function "mood" to the local REPL's context.
local.context.mood = mood;

r3

Albert Latacz ha la soluzione per Java: Java REPL – Java REPL is a simple Read-Eval-Print-Loop for Java language. Lo vedrà GG, prossimamente.

OK, finora abbiamo visto linguaggi non proprio miei (GG prossimamente, dopo le ferie, …) ma ecco Python, su Stack Overflow: How to implement a Python REPL that nicely handles asynchronous output?
Sembra interessante.
Il post fa riferimento a un modulo ad hoc, sclapp ma non risulta più attivo, benché presente (non all’URL indicato); non provato.
Con qualche aggiornamento essendo codice scritto per la versione 2.x, ecco r-so.py:

#!/usr/bin/python3

import readline
import threading

PROMPT = '> '

def interrupt():
    print(readline.get_line_buffer(), sep='', end='')

def cli():
    print('Interrupting cow -- moo!')
    while True:
        cli = str(input(PROMPT))
        print(cli)
        if cli.strip() == 'q!': exit(0)

if __name__ == '__main__':
    threading.Thread(target=cli).start()
    threading.Timer(2, interrupt).start()

r4

Sembra tutto OK; la riga print(cli) va sostituita con un case (se si usa Coconut) o un if colossale che è meglio mettere in una funzione. Da completare, prossimamente…
Però c’è anche questo: cmd – Create line-oriented command processors.
Il modulo cmd è tra quelli automaticamente installati con Python.

Da provare prima di subito; questo è l’esempio del post (r-cmd.py):

#!/usr/bin/python3

import cmd

class HelloWorld(cmd.Cmd):
    """Simple command processor example."""
    
    def do_greet(self, line):
        print("hello")
    
    def do_EOF(self, line):
        return True
    
if __name__ == '__main__':
    HelloWorld().cmdloop()

r5

Vediamo se ho capito, personalizzo (r-cmd-mod.py):

#!/usr/bin/python3

import cmd
import os

class HelloWorld(cmd.Cmd):
    """Simple command processor example."""
    
    def preloop(self):
        self.prompt = "$ > "
    
    def do_greet(self, line):
        print("hello")
    
    def do_EOF(self, line):
        print("")
        return True
    
    def do_mycmd(self, line):
        os.system("pwd;date")
        
if __name__ == '__main__':
    HelloWorld().cmdloop()

r6

😀 OK!

Conclusione
Si può fare, chissà se si farà, chissà se il mio giovane collaboratore über-nerd… Per adesso l’ho messo tra le cose da fare, quasi urgenti ma non urgenti per davvero😉

:mrgreen:

Coconut – Espressioni

lovelace

Ho fatto un po’ di prove e Coconut funziona😀
Deve crescere ma mi sa che lo userò per compiti particolari (p.es.: case) intanto continuo a studiarlo, copiando qui.

Lazy lists
Coconut supports the creation of lazy lists, where the contents in the list will be treated as an iterator and not evaluated until they are needed. Lazy lists can be created in Coconut simply by simply surrounding a comma-seperated list of items with (| and |) (so-called “banana brackets”) instead of [ and ] for a list or ( and ) for a tuple.

Lazy lists use the same machinery as iterator chaining to make themselves lazy, and thus the lazy list (| x, y |) is equivalent to the iterator chaining expression (x,) :: (y,), although the lazy list won’t construct the intermediate tuples.

Lazy lists, where sequences are only evaluated when their contents are requested, are a mainstay of functional programming, allowing for dynamic evaluation of the list’s contents.

c40

In Python can’t be done without a complicated iterator comprehension in place of the lazy list. See the compiled code for the Python syntax.
Ecco un altro caso in cui torna utile😀

Applicazione parziale implicita
Coconut supports a number of different syntactical aliases for common partial application use cases. These are:

.attr         =>  operator.attrgetter("attr")
.method(args) =>  operator.methodcaller("method", args)
obj.          =>  getattr$(obj)
func$         =>  ($)$(func)
seq[]         =>  operator.__getitem__$(seq)
iter$[]       =>  # the equivalent of seq[] for iterators

c41

Vuoi vedere che manca un modulo…

c42

OK, come previsto😀

Set Literals
Coconut allows an optional s to be prepended in front of Python set literals. While in most cases this does nothing, in the case of the empty set it lets Coconut know that it is an empty set and not an empty dictionary. Additionally, an f is also supported, in which case a Python frozenset will be generated instead of a normal set.

c43

Imaginary Literals
In addition to Python’s <num>j or <num>J notation for imaginary literals, Coconut also supports <num>i or <num>I, to make imaginary literals more readable if used in a mathematical context.

Imaginary literals are described by the following lexical definitions:
imagnumber ::= (floatnumber | intpart) ("j" | "J" | "i" | "I")

An imaginary literal yields a complex number with a real part of 0.0. Complex numbers are represented as a pair of floating point numbers and have the same restrictions on their range. To create a complex number with a nonzero real part, add a floating point number to it, e.g., (3+4i). Some examples of imaginary literals:
3.14i   10.i    10i     .001i   1e100i  3.14e-10i

c44

Underscore Separators
Coconut allows for one underscore between digits and after base specifiers in numeric literals. These underscores are ignored and should only be used to increase code readability. Comodo, pochi linguaggi lo usano, p.es. Ada.

c45

:mrgreen:

 

Il Fortran iniziale, I/O, FORMAT

wo-the-wizard

Il titolo è fuorviante, riassumo una conversazione con un collega dei tempi antichi riguardo al post Lo sviluppo iniziale dei linguaggi di programmazione.
Naturalmente riguarda il Fortran l’unico linguaggio che c’era sul ‘puter che usavamo. Il programma postato da Knuth e Trabb Pardo (K&T) è per la versione 0 (zero), forse solo virtuale. La prima versione con cui ho avuto a che fare è la IV, anzi un’evoluzione compatibile con la 66 ma c’era codice scritto per la versione II (1958), trasferito da schede a nastri o dischi o semplicemente ricopiato dal listato.
In ogni caso il programma postato da K&T deve essere aggiornato (ehi! 50 anni!): non solo le funzioni non hanno (dalla preistoria) il nome che deve terminare con F ma non ho nemmeno un lettore di schede😦
E non saprei nemmeno trovare le schede😦 e la perforatrice😦

forig

Però il programma prevede la lettura di dati formattati in modo preciso, cosa che da terminale non è agevole (bisogna essere proprio pistini) ma si può fare un formattatore, come i millemila come si usava una volta.

Ah! un altro problema: la gestione dei file: allora si aprivano da OS, il programma lo dava per scontato e alla fine si chiudevano, sempre da OS. I comandi Prime erano rispettivamente OPEN seguito dai parametri che non ricordo ma roba tipo 1 1 (unità 5 in lettura [aggiornamento: il secondo parametro è da verificare, probamilente non come pensavo]) e CLOSE 1 (unità 5); siccome di solito si dovevano chiudere più unità c’era il comodo CLOSE ALL. Su Prime (come con il DEC) i comandi potevano essere abbreviati, nel nostro caso al posto di OPEN bastava O, era il comando più importante iniziante con quella lettera, e C stava per CLOSE.
Quindi, se ricordo bene, il mio command-file per l’esecuzione del programma FMT sarebbe stato così:

O DATI-FREE 1 1
O DATI 2 2
SEG FMT
C ALL

L’istruzione di lancio SEG FMT valeva nell’ipotesi che il programma fosse stato compilato per la versione segmentata (prevista per i programmi grossi) usata (a sproposito) abitualmente. Però se uno era razionale vedeva che bastava la versione normale, anche più veloce quindi la riga sarebbe stata RUN FMT, RUN abbreviabile in R. OK, mode nostalgia OFF.

La versione corrente usa le normali istruzioni OPEN e CLOSE, non disponibili allora. Per evidenziare il nuovo uso il minuscolo, adesso equivalente al maiuscolo, allora vietato.

Ecco fmt.f

C FMT legge in free-format e scrive formattato

      DIMENSION A(11)
      
      open(1, file='dati-free')
      READ(1, *) A
      close(1)
      
      open(2, file='dati')
      WRITE(2, 2) A
      close(2)
2     FORMAT(6F12.4)

      END

Aggiornamento: per il Prime le prime 4 unità di I/O erano predefinite e riservate per cui avrei dovuto usare numeri più alti, tipicamente 5 e 6 al posto di 1 in OPEN e 2 in WRITE.

E questo è il file di dati dati-free

1 10 100 1000 10000 98765 987654
987654.3 2.7818 3.1415 -42

Compilo ed eseguo

f0

ottenendo il file dati

      1.0000     10.0000    100.0000   1000.0000  10000.0000  98765.0000
 987654.0000 987654.3125      2.7818      3.1415    -42.0000

Uh! visto il secondo dato della seconda riga? è 987654.3125 invece del previsto 987654.3000😳 ma il formato REAL*4 (quello usato per le variabili inizianti con una lettera fuori dell’intervallo [I-N] riservato agli interi) garantisce 7-8 cifre significative.

Per essere proprio sicuri-sicuri si può fare la verifica, con il file verifica.f

C VERIFICA

      DIMENSION A(11)
      
      open(1, file='dati')
      READ(1, 2) A
      close(1)
2     FORMAT(6F12.4)

      DO 3 I = 1, 11
        PRINT *, A(I)
3     CONTINUE     

      END

f1

OK, ci siamo. A questo punto torno al programma di K&P, opportunamente aggiornato (tpk.f).

C     THE TPK ALGORHTM, FORTRAN STYLE
*     aggiornato, modifiche in minuscolo

      FUN(T) = SQRT(ABS(T)) + 5.0**3
      DIMENSION A(11)
1     FORMAT(6F12.4)
      open(1, file = 'dati') 
      READ(1, 1) A
      close(1)
      DO 10 J =1, 11
      I = 11 - J
      Y = FUN(A(I+1))
      IF(400.0 - Y) 4, 8, 8
4     write(*, 5) I
5     FORMAT(I10, 10H TOO LARGE)
      GO TO 10
8     write(*, 9) I, Y
9     FORMAT(I10, F12.7)
10    CONTINUE
      STOP 52525
      end

f2

OK😀

C’è ancora una cosa emersa ieri: le funzioni, quando non ci sono si fanno. A dire tutta la verità i fortrainers preferivano –di gran lunga– le SUBROUTINEs ma questo sarebbe un altro rant, lunghissimissimo😉

Era tipico di IBM fornire funzioni che non si avevano (no, niente nomi). Per gioco supponiamo non fossero definite ABS e SQRT.
La prima è banale (e lasciata come esercizio) mentre la seconda è leggermente più impegnativa. Siccome il mondo è vario da adesso in poi continuo con Python 3.x, il Basic dei nostri giorni.

Invece di limitarci alla radice quadrata conviene probabilmente essere più generali; che ne dite di usare i logaritmi? OK, la Wiki (come faremmo senza?) ci dice tutto. Illo tempore probabilmente avrei cercato sul Dwight –hey! c’è online–, c’è il capitolo Logarithmic Functions, p.130
log-x
Per l’esponenziale c’è Exponential Functions a p.125

exp-x

Quindi ecco myfunc.py:

# modulo per Python 3.x; 
# per 2.x importare division from tuture

my_e = 2.71828182845904523536 

def my_log(x):
    r = (x - 1) / (x + 1)
    d = r
    v = d
    c = 1
    while d > 1e-8:
        c += 2
        t = 1
        for i in range(1, c + 1):
            t *= r
        d = 1/c * t
        v += d

    return round(2 * v, 8)

def my_exp(x):
    fact = 1
    t = x/fact
    e = 1 + t
    num = x
    c = 1
    while t > 1.e-8:
        c += 1
        fact *= c
        num *= x
        t = num / fact
        e += t
    st = str(e)
    ppoint = st.index(".")
    if ppoint < 8:
        e = round(e, 8 - ppoint)
    else:
        e = round(e, 1) 

    return e
    
def my_sqrt(x):
    return my_exp(1/2 * my_log(x))

py0

:mrgreen:

Una risposta & una domanda

woz

Una risposta a Jilla, forse se lo saranno chiesto anche altri: Coconut è nuovo ma promette bene. E l’uso che immediatamente viene in mente è quello di creare moduli con dentro le costanti e le funzioni che si intende usare, visto che permette di scrivere chiaramente e sinteticamente cose che con Python sono meno immediate, p.es. case.
Ecco un esempio (banalissimo, certamente nessuno scriverebbe cose così per davvero) di quali sono le mie intenzioni.

Il modulo coco_module.coco:

risposta = 42

def ciao(nome):
    print("Ehi", nome, "ciao!")

def doppio(n):
    return 2 * n

Che compilo ottenendo il modulo Python

r0

Non importa che coco_module.py sia lungo 298 righe, non devo editarlo, solo usarlo, nello script uso_c_m.py:

from coco_module import *

ciao("Juhan")
print(risposta, doppio(doppio(doppio(risposta))))

r1

Ovviamente –come già detto– quando il gioco si fa duro (inizio-cit.).

OK, lo ammetto: Coconut mi piace per i suoi aspetti funzionali che adesso pare siano trendyassay, quasi fossero pokémons. Forse perché contemporaneamente sto vedendo quell’altro linguaggio, di cui al momento non ricordo il nome, molto più tradizionale (nato come alternativa semplice del Fortran).


Siccome ho risposto, spero esaurientemente, alla domanda fattami ne avrei una anch’io (me). Chissà se…
Viene da un tweet, Why Linux sucks and will never compete with Windows or OSX.

Non so chi l’ha retwittato ma ho voluto vedere cos’è che non va con Linux.
Inizialmente ho classificato non tanto bene il post, l’autore del post, il titolare del blog, il retwittatore &co. poi però –in modo non esattamente razionale– ci ho rimuginato su. Non ho capito se l’autore sta lollando o cosa. Non ho capito cosa non gli va di Linux, sa usarlo, lo usa da tempo. Viene anche il dubbio che sia al servizio di NWO o Bilderberg o anche peggio (M$).

Sperando di non fargli pubblicità (segue 3 ed è seguito da 4 ma ha un blog) la mia domanda è: che dire del tipo e del tipo che ha linkato?

:mrgreen:

Coconut – keywords II

handshake

Continuo con le keywords, da qui, copiando qui.

case
Coconut’s case statement is an extension of Coconut’s match statement for performing multiple match statements against the same value, where only one of them should succeed. Unlike lone match statements, only one match statement inside of a case block will ever succeed, and thus more general matches should be put below more specific ones.

Each pattern in a case block is checked until a match is found, and then the corresponding body is executed, and the case block terminated. The syntax for case blocks is

case <value>:
    match <pattern> [if <cond>]:
        <body>
    match <pattern> [if <cond>]:
        <body>
    ...
[else:
    <body>]

where <pattern> is any match pattern, <value> is the item to match against, <cond> is an optional additional check, and <body> is simply code that is executed if the header above it succeeds. Note the absence of an in in the match statements: that’s because the <value> in case <value> is taking its place.

c36

In Python can’t be done without a long series of checks for each match statement. See the compiled code for the Python syntax.

c37

Al solito codice difficile da leggere, peraltro OK:

c38

Backslash-Escaping
In Coconut, the keywords data, match, case, async (keyword in Python 3.5), and await (keyword in Python 3.5) are also valid variable names. While Coconut can disambiguate these two use cases, when using one of these keywords as a variable name, a backslash is allowed in front to be explicit about using a keyword as a variable name.

c39

Variabili riservate
In Coconut, all variable names starting with _coconut are reserved. The Coconut compiler will modify and reference these variables with the assumption that the code being compiled does not modify them in any way. If your code does modify any such variables, your code is unlikely to work properly.

:mrgreen:

Variabili: uso e abuso

CiEUn tweet di un programmatore funzionale che riporta un tweet che –OK, è qui.

v0

OK, pythonisticamente è corretto. [2] è una lista contenente l’intero 2.

v1

In Python, come in taaanti linguaggi ‘normali‘ le variabili sono –come dice il nome– variabili e possono essere ridefinite.
Ma forse Python esagera, consideriamo il ciclo seguente:

v2

Sembra tutto OK, vero?
Ma forse… approfondiamo:

v3

Uhmmm… qui l’indice del ciclo viene modificato all’interno del ciclo stesso ma ciò non turba l’interprete che al giro successivo sa di dover usare l’elemento successivo della lista. La stessa cosa capita se usiamo range(), che infatti produce la lista usata dal ciclo:

v4

OK😀

Fin qui è tutto abbastanza scontato, tranne per i nuovi che usano solo linguaggi strettamente funzionali (un po’ li invidio) ma a me che sono vecchio è tornata in mente una cosa di tanto tempo fa. Adesso ve la racconto.
Illo tempore ogni computer aveva il suo sistema operativo, proprietario e di solito diverso da tutti gli altri. Anche i compilatori, di sicuro il Fortran e pochi altri. Quando è nato il PC IBM con il DOS di Microsoft è successo che (numero dei sistemi operativi)++, nient’altro; e peraltro tutt’altro che buono. Ma sto divagando.

Riprendo l’esempio che appalla i funzionali, in Fortran 77, quello corrente di allora si ha (uso i al posto di x per le convenzioni implicite del linguaggio):

* c0.f
      i = 1
      do 10, i = 2, 4
          print *, i
10    end do
      print *, i      
      
      end

v5

OK, a parte s/for/do/ e * per dire ‘default’ in print tutto come previsto.

Occorre anche considerare una cosa che tutti i fortrainers davano per scontato: all’uscita del ciclo il contatore è incrementato. Ma è inaffidabile, tra l’altro varia rispetto alla versione precedente del compilatore (Fortran IV).
Vediamo se si può, come in Python, …

* c1.f
      i = 1
      do 10, i = 2, 4
          i = 2 * i
          print *, i
10    end do
      print *, i
      
      end

v6No, nope! come previsto il compilatore GNU segnala la violazione.

Ci può essere un caso più sottile, quello capitatomi (io ero il debugger):

* c2.f
      i = 1
      do 10, i = 2, 4
          call doppio(i)
10    end do
      print *, i
      
      end
      
      subroutine doppio(n)
      n = 2 * n
      print *, n
      end

v7

OOPS!😳 cos’è successo? I fortrainers lo sanno: le variabili vengono sempre passate per indirizzo a funzioni e subroutines. In questo caso l’indice del ciclo viene modificato all’interno del ciclo stesso ma il compilatore non può saperlo. È cura del programmatore –che queste cose le dovrebbe sapere– diminuire il carico di lavoro al debugger, sempre che il bug venga rilevato, possibilmente presto😀

Al programmatore era stato senz’altro stato detto anche “cambia il nome alle variabili” che è una frase ce dev’essere interpretata e tradotta in linguaggio normale. Vediamola applicata:

* c3.f
      i = 1
      do 10, i = 2, 4
          call doppio(i)
10    end do
      print *, i
      
      end
      
      subroutine doppio(k)
      n = k
      n = 2 * n
      print *, n
      end

v8OK😀 C’era un altro motivo per cambiare le variabili, cosa che oggi probabilmente ha meno senso: le variabili locali sono near pointers mentre le globali sono far pointers. Non credo di dire cose nuove, ma forse dimenticate al momento opportuno😉

:mrgreen:

Coconut – keywords I

proxy

Continuo con le keywords, da qui, copiando qui.

match

Coconut provides fully-featured, functional pattern-matching through its match statements.

Overview
match statements follow the basic syntax match <pattern> in <value>. The match statement will attempt to match the value against the pattern, and if successful, bind any variables in the pattern to whatever is in the same position in the value, and execute the code below the match statement. match statements also support, in their basic syntax, an if <cond> that will check the condition after executing the match before executing the code below, and an else statement afterwards that will only be executed if the match statement is not. What is allowed in the match statement’s pattern has no equivalent in Python, and thus the specifications below are provided to explain it.

Syntax Specification
Coconut match statement syntax is

match <pattern> in <value> [if <cond>]:
    <body>
[else:
    <body>]

where <value> is the item to match against, <cond> is an optional additional check, and <body> is simply code that is executed if the header above it succeeds. <pattern> follows its own, special syntax, defined roughly like so:

pattern ::= (
    "(" pattern ")"                 # parentheses
    | "None" | "True" | "False"     # constants
    | "=" NAME                      # check
    | NUMBER                        # numbers
    | STRING                        # strings
    | [pattern "as"] NAME           # capture
    | NAME "(" patterns ")"         # data types
    | "(" patterns ")"              # sequences can be in tuple form
    | "[" patterns "]"              #  or in list form
    | "(|" patterns "|)"            # lazy lists
    | "{" pattern_pairs "}"         # dictionaries
    | ["s"] "{" pattern_consts "}"  # sets
    | (                             # head-tail splits
        "(" patterns ")"
        | "[" patterns "]"
      ) "+" pattern
    | pattern "+" (                 # init-last splits
        "(" patterns ")"
        | "[" patterns "]"
      )
    | (                             # head-last splits
        "(" patterns ")"
        | "[" patterns "]"
      ) "+" pattern "+" (
        "(" patterns ")"            # this match must be the same
        | "[" patterns "]"          #  construct as the first match
      )
    | (                             # iterator splits
        "(" patterns ")"
        | "[" patterns "]"
        | "(|" patterns "|)"        # lazy lists
      ) "::" pattern
    | pattern "is" exprs            # type-checking
    | pattern "and" pattern         # match all
    | pattern "or" pattern          # match any
    )

😳 panico?

Semantic Specification
match statements will take their pattern and attempt to “match” against it, performing the checks and deconstructions on the arguments as specified by the pattern. The different constructs that can be specified in a pattern, and their function, are:

  • Constants, Numbers, and Strings: will only match to the same constant, number, or string in the same position in the arguments.
  • Variables: will match to anything, and will be bound to whatever they match to, with some exceptions:
    • If the same variable is used multiple times, a check will be performed that each use match to the same value.
    • If the variable name _ is used, nothing will be bound and everything will always match to it.
  • Explicit Bindings (<pattern> as <var>): will bind <var> to <pattern>.
  • Checks (=<var>): will check that whatever is in that position is equal to the previously defined variable <var>.
  • Type Checks (<var> is <types>): will check that whatever is in that position is of type(s) <types> before binding the <var>.
  • Data Types (<name>(<args>)): will check that whatever is in that position is of data type <name> and will match the attributes to <args>.
  • Lists ([<patterns>]), Tuples ((<patterns>)), or lazy lists ((|<patterns>|)): will only match a sequence (collections.abc.Sequence) of the same length, and will check the contents against <patterns>.
  • Dicts ({<pairs>}): will only match a mapping (collections.abc.Mapping) of the same length, and will check the contents against <pairs>.
  • Sets ({<constants>}): will only match a set (collections.abc.Set) of the same length and contents.
  • Head-Tail Splits (<list/tuple> + <var>): will match the beginning of the sequence against the <list/tuple>, then bind the rest to <var>, and make it the type of the construct used.
  • Init-Last Splits (<var> + <list/tuple>): exactly the same as head-tail splits, but on the end instead of the beginning of the sequence.
  • Head-Last Splits (<list/tuple> + <var> + <list/tuple>): the combination of a head-tail and an init-last split.
  • Iterator Splits (<list/tuple/lazy list> :: <var>, or <lazy list>): will match the beginning of an iterable (collections.abc.Iterable) against the <list/tuple/lazy list>, then bind the rest to <var> or check that the iterable is done.

When checking whether or not an object can be matched against in a particular fashion, Coconut makes use of Python’s abstract base classes. Therefore, to enable proper matching for a custom object, register it with the proper abstract base classes.

Examples:

c31

Showcases else statements, which work much like else statements in Python: the code under an else statement is only executed if the corresponding match fails.

c32

Showcases matching to data types. Values defined by the user with the data statement can be matched against and their contents accessed by specifically referencing arguments to the data type’s constructor.

c33

Showcases how the combination of data types and match statements can be used to powerful effect to replicate the usage of algebraic data types in other functional programming languages.

c34

Showcases head-tail splitting, one of the most common uses of pattern-matching, where a + <var> (or :: <var> for any iterable) at the end of a list or tuple literal can be used to match the rest of the sequence.
E naturalmente il gioco può continuare (l’idea della pipe, Douglas McIlroy et al. (tanti al. anche prima) è fantastica):

c35

With Pythoncan’t be done without a long series of checks for each match statement. See the compiled code for the Python syntax.
Un’osservazione non so quanto valida: il codice prodotto dalla compilazione di Coconut è sempree molto poco leggibile, OK se è finale, altrimenti richiede pazienza, assay😉

:mrgreen:

Coconut – operatori II

1892_5

Ci sono argomenti istituzionali come Octave (sì siamo in ritardo ma il caldo, le ferie, …) e ci sono argomenti che forse poi –chissà– come Coconut. Poi ce ne sono anche altri ancora, come SICP, che meriterebbero più attenzione. Avendo tempo. OK, oggi Coconut, continuo da qui a copiare qui.

Iterator Slicing
Coconut uses a $ sign right after an iterator before a slice to perform iterator slicing. Coconut’s iterator slicing works much the same as Python’s sequence slicing, and looks much the same as Coconut’s partial application, but with brackets instead of parentheses. It has the same precedence as subscription.
Iterator slicing works just like sequence slicing, including support for negative indices and slices, and support for slice objects in the same way as can be done with normal slicing. Iterator slicing makes no guarantee, however, that the original iterator passed to it be preserved (to preserve the iterator, use Coconut’s tee function).
Coconut’s iterator slicing is very similar to Python’s itertools.islice, but unlike itertools.islice, Coconut’s iterator slicing supports negative indices, and is optimized to play nicely with custom or built-in sequence types as well as Coconut’s map, zip, range, and count objects, only computing the elements of each that are actually necessary to extract the desired slice. This behavior can also be extended to custom objects if they define their __getitem__ method lazily and set __coconut_is_lazy__ to True.

c24

Nota: il manuale usa 10**100 ma mi da overflow😳

With Python can’t be done without a complicated iterator slicing function and inspection of custom objects. The necessary definitions in Python can be found in the Coconut header.

Alternative Unicode
Coconut supports Unicode alternatives to many different operator symbols. The Unicode alternatives are relatively straightforward, and chosen to reflect the look and/or meaning of the original symbol.

→ (\u2192)                  => "->"
↦ (\u21a6)                  => "|>"
*↦ (*\u21a6)                => "|*>"
↤ (\u21a4)                  => "<|" 
↤* (\u21a4*)                => "<*|" 
⋅ (\u22c5)                  => "*"
↑ (\u2191)                  => "**"
÷ (\xf7)                    => "/"
÷/ (\xf7/)                  => "//"
∘ (\u2218)                  => ".."
− (\u2212)                  => "-" (only subtraction)
⁻ (\u207b)                  => "-" (only negation)
¬ (\xac)                    => "~"
≠ (\u2260) or ¬= (\xac=)    => "!="
≤ (\u2264)                  => "<=" 
≥ (\u2265)                  => ">="
∧ (\u2227) or ∩ (\u2229)    => "&"
∨ (\u2228) or ∪ (\u222a)    => "|"
⊻ (\u22bb) or ⊕ (\u2295)    => "^"
« (\xab)                    => "<<" 
» (\xbb)                    => ">>"
… (\u2026)                  => "..."
× (\xd7)                    => "@" (only matrix multiplication)

c25

OK, ma forse –sono vecchio– perché cambiare. E poi non è comodissimo introdurre il codice dopo C-S-u. Non sono riuscito a usare quelli con il codice esadecimale, per esempio xf7.

Keywords

data
The syntax for data blocks is a cross between the syntax for functions and the syntax for classes. The first line looks like a function definition, but the rest of the body looks like a class, usually containing method definitions. This is because while data blocks actually end up as classes in Python, Coconut automatically creates a special, immutable constructor based on the given arguments.

Coconut data blocks create immutable classes derived from collections.namedtuple and made immutable with __slots__. Coconut data statement syntax looks like:

data <name>(<args>):
    <body>

<name> is the name of the new data type, <args> are the arguments to its constructor as well as the names of its attributes, and <body> contains the data type’s methods.Subclassing data types can be done easily by inheriting from them in a normal Python class, although to make the new subclass immutable, the line

__slots__ = ()

Rationale
A mainstay of functional programming that Coconut improves in Python is the use of values, or immutable data types. Immutable data can be very useful because it guarantees that once you have some data it won’t change, but in Python creating custom immutable data types is difficult. Coconut makes it very easy by providing data blocks.

Python Docs
Returns a new tuple subclass. The new subclass is used to create tuple-like objects that have fields accessible by attribute lookup as well as being indexable and iterable. Instances of the subclass also have a helpful docstring (with type names and field names) and a helpful __repr__() method which lists the tuple contents in a name=value format.

Any valid Python identifier may be used for a field name except for names starting with an underscore. Valid identifiers consist of letters, digits, and underscores but do not start with a digit or underscore and cannot be a keyword such as class, for, return, global, pass, or raise.

Named tuple instances do not have per-instance dictionaries, so they are lightweight and require no more memory than regular tuples.

c26

This will fail because data objects are immutable

c27

la versione Python

c28

Showcases the syntax, features, and immutable nature of data types.

c29

Nota: la riga dopo il decoratore viene scritta in modo bizzarro nella REPL, quella corretta è def size(Node(left, right)) = size(left) + size(right) ovviamente.

la versione Python

c30

Pausa, devo digerire il tutto; poi continuo sempre sulle keywords😀
:mrgreen:

pdb – debugger per Python

tapacolo

Doug Hellmann (che trovate qui e qui e forse anche da altre parti) mi dice che c’è pdb, debugger interattivo per Python, cosa che non sapevo.

E pensare che il modulo arriva preinstallato con Python (sia 2.7 che 3.x). Vero che:

This is currently undocumented but easily understood by reading the source.

Siccome Doug spiega tutto per bene potrei chiudere qui ma il post sarebbe troppo corto😳 e allora metto giù un paio di screenshots, quelli delle mie prove iniziali.
Finora mi sono limitato a mettere dei print dove verosimilmente dovrebbero esserci breakpoints: entrata delle finzioni che sto testando, valori che ritornano e così via.
Con i debuggers ho passato parecchio tempo, prima su Prime poi su Apollo e Unix. Finché ho scoperto la meraviglia dell’ambiente integrato (inizialmente con Turbo Pascal 3.0) e poi con i vari Basic di Microsoft.

Ma nel frattempo avevo visto che conveniva –di molto– scrivere in modo da minimizzare la necessità di ricorrere al debug; anche perché non sempre c’è (o non so che c’è). Che è poi quello che dice Daniel Lemire: I do not use a debugger.

Ma mai dire mai, un tool in più che può sempre servire.
A me piace partire al volo con l’equivalente di “Hello World!” che per il debug è qualcosa di simile (d0.py):

#!/usr/bin/python3

m = 7
n = 5
s = m + n
print("somma", m, n , s)
d = m - n
r = m / n
ri = m // n
print("- & /", d, r, ri)

d0

Il comando w (where) visualizza la linea corrente, per il codice si usa l (list), q per quit.

d1

Hey! mi sembra di essere tornato indietro di 35 anni, il comando s (step) e la sessione è com’era su Prime; vero che allora dovevi compilare con l’opzione -d e linkare forse con una libreria apposita (non ricordo, è passato un po’ di tempo).

Dentro all’interprete ha senso solo per i moduli, uso l’esempio di Doug (ddh.py):

#!/usr/bin/env python3
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann.  All rights reserved.
#

class MyObj:

    def __init__(self, num_loops):
        self.count = num_loops

    def go(self):
        for i in range(self.num_loops):
            print(i)
        return

d2

e così via.

OK, adesso a fare le cose come si deve dovrei fare l’elenco dei comandi, far vedere come settare un breakpoint, andarci, esaminare lo stack e roba simile. Una cosa tremendamente poco sexy che spero davvero di non dover fare mai. Molto diversa dall’IDE del Borland Pascal (e simili)😀

:mrgreen:

Iscriviti

Ricevi al tuo indirizzo email tutti i nuovi post del sito.

Segui assieme ad altri 95 follower