One-liner

programma shell contenuto in una sola riga
Nota disambigua.svg Disambiguazione – Se stai cercando la battuta teatrale, vedi One-liner (battuta).

In informatica uno one-liner (traducibile dall'inglese come "mono-linea") è un input di un'unica riga di codice fornita ad un interprete di comandi per eseguire un determinato compito. Il termine viene usato per estensione per indicare un programma in un qualsiasi linguaggio che svolge una funzione non banale in una sola riga di codice. Uno one-liner è tipicamente scritto nel linguaggio della shell sulla quale viene eseguito oppure invoca un altro interprete e gli passa il codice da eseguire, più raramente invoca un compilatore, fornendo il sorgente da compilare e le istruzioni per eseguire il software compilato.

Alcuni linguaggi dinamici di scripting come awk, sed e Perl sono tradizionalmente usati per scrivere one-liner. Gli one-liner sono usati talvolta per mostrare l'espressività di un linguaggio di programmazione, o l'abilità di un programmatore, ed esistono competizioni il cui obiettivo è creare one-liner particolarmente compatti ed espressivi.

StoriaModifica

L'espressione one-liner appare due volte nell'indice del libro The AWK Programming Language[1] (noto con l'acronimo TAPL) di Alfred Aho, Peter Weinberger e Brian Kernighan, il testo di riferimento più significativo per il linguaggio awk. Gli autori spiegano la nascita del paradigma one-liner nel loro lavoro sulle prime macchine Unix:

(EN)

«The 1977 version had only a few built-in variables and predefined functions. It was designed for writing short programs [...] Our model was that an invocation would be one or two lines long, typed in and used immediately. Defaults were chosen to match this style [...] We, being the authors, knew how the language was supposed to be used, and so we only wrote one-liners.»

(IT)

«La versione 1977 aveva solo poche variabili built-in e funzioni predefinite. Era progettata per scrivere brevi programmi [...] Il nostro modello prevedeva che l'invocazione fosse lunga una o due righe, inserita e utilizzata immediatamente. Le impostazioni predefinite sono state scelte per adeguarsi a tale stile [...] Noi, essendo gli autori, sapevamo come si supponeva che il linguaggio avrebbe dovuto essere usato, e di conseguenza scrivemmo solo one-liner

(The AWK Programming Language)

Questa prima definizione di one-liner implica l'immediata esecuzione del programma senza compilazione, quindi si riferirebbe solo al codice sorgente di linguaggi interpretati. Tuttavia, nel 1985 è stata aggiunta nel IOCCC una categoria per i Best One Liner in linguaggio C, che è invece compilato.

EsempiModifica

AWKModifica

Il libro The AWK Programming Language contiene venti esempi di one-liner[2] al termine del primo capitolo. Tra essi vi sono i seguenti:

  1. Stampa il numero totale di righe dell'input:
    END { print NR }
  2. Stampa la decima riga in input:
    NR == 10
  3. Stampa l'ultimo campo di ogni riga in input:
    { print $NF }

CModifica

Il seguente è un esempio di one-liner in C (vincitore di un "Best one-liner" IOCCC).

main(int c,char**v){return!m(v[1],v[2]);}m(char*s,char*t){return*t-42?*s?63==*t|*s==*t&&m(s+1,t+1):!*t:m(s,t+1)||*s&&m(s+1,t);}

Si tratta di un glob pattern matcher, che riconosce i caratteri '*' (zero o più caratteri) e '?' (esattamente un carattere), analogamente alla maggior parte delle shell Unix. Viene eseguito con due parametri, una stringa e un pattern, e l'exit status è 0 se si verifica il match del pattern, 1 altrimenti. Il pattern deve identificare l'intera stringa, per identificare una sottostringa basta aggiungere un asterisco all'inizio e alla fine del pattern. Esempi d'uso:

 $ prog foo 'f??'; echo $?
 $ prog 'best short program' '??st*o**p?*'; echo $?

HaskellModifica

Il seguente one-liner in Haskell effettua l'ordinamento lessicografico (secondo l'ordine ASCII) delle linee in input.[3]

 main = (mapM_ putStrLn . Data.List.sort . lines) =<< getContents

Una versione ulteriormente compatta:

 main = interact (unlines . Data.List.sort . lines)

Invocabile da riga di comando con:

 cat filename | ghc -e "interact (unlines . Data.List.sort . lines)"

JModifica

Alcuni esempi in J:

  1. Funzione avg che restituisce la media di una lista di numeri:
    avg=: +/ % #
  2. Quicksort:
    quicksort=: (($:@(<#[) , (=#[) , $:@(>#[)) ({~ ?@#)) ^: (1<#)
    

OCamlModifica

Una funzione che verifica il checksum con la formula di Luhn su una lista di interi.

 let luhn l = fst (List.fold_right (fun x (a,s) -> a+s*x/10+s*x mod 10, 3-s) l (0,1)) mod 10 = 0

PerlModifica

Alcuni esempi in Perl:

  • Ricerca di parole duplicate
perl -0777 -ne 'print "$.: doubled $_\n" while /\b(\w+)\b\s+\b\1\b/gi' 
perl -lne 'print if $_ eq reverse' /usr/dict/words
  • Sostituisci la stringa "foo" con la stringa "bar" nei file locali con estensione .c:
perl -p -i.bak -e 's/\bfoo\b/bar/g' *.c
  • Inverti tutti i byte in un file:
 perl -0777e 'print scalar reverse <>' filename

Molti one-liner in Perl sono imperativi, ma il supporto di Perl di funzioni anonime, closure, mappe, filtri (grep) e fold (List::Util::reduce) permette la creazione di one-liner funzionali. Ad esempio il seguente one-liner definisce una funzione che restituisce una lista csv dei primi fino al valore passato come parametro:

my $z = sub { grep { $a=$_; !grep { !($a % $_) } (2..$_-1)} (2..$_[0]) }

Può essere invocata da riga di comando nel modo seguente:

perl -e'$,=",";print sub { grep { $a=$_; !grep { !($a % $_) } (2..$_-1)} (2..$_[0]) }->(shift)' n

per stampare una lista dei primi compresi tra 2 e n.

PowerShellModifica

Un esempio di one-liner in PowerShell che trova i palindromi nel file words.txt:

cat words.txt | %{if($_ -eq ($_[-1..-($_.length)] -join '')){$_}}

Questo esempio prende in input una lista di nomi e valori in un file csv, e restituisce la somma dei valori corrispondenti a ciascun nome:

ipcsv .\fruit.txt H F, C|Group F|%{@{"$($_.Name)"=($_.Group|measure C -sum).Sum}}|sort value

PythonModifica

La seguente riga di Python definisce la funzione qs, che esegue il quicksort di un array:

 qs = lambda p=None, *l: [] if p is None else qs(*[x for x in l if x<p]) + [p] + qs(*[x for x in l if x>=p])

Python non è un linguaggio in forma libera e le istruzioni sono separate dal ritorno a capo, inoltre le lambda possono contenere un'unica espressione, per cui realizzare one-liner in un programma può non essere sempre immediato. Gli one-liner possono essere realizzati agevolmente a riga di comando, usando il flag -cmd (o l'alias abbreviato -c), e tipicamente richiedono l'import di uno o più moduli. Le istruzioni sono separate con ";" invece che con il consueto ritorno a capo. Ad esempio, per stampare l'ultimo campo dello standard input:

ls -l | python -c "import sys;[sys.stdout.write(' '.join([line.split(' ')[-1]])) for line in sys.stdin]"

Sono stati sviluppati diversi script per facilitare la realizzazione di one-liner in Python, come pyp[4] o Pyline[5], che importano i moduli di uso comune e forniscono variabili e funzionalità più human-readable. Ad esempio:

ls -l | pyp "whitespace[-1]"  # "whitespace" contiene i token separati da whitespace in pyp
ls -l | pyline "words[-1]"    # "words" contiene i token separati da whitespace in pyline

RacketModifica

Il seguente programma Racket è equivalente al precedente esempio in Haskell:

#lang racket
(for-each displayln (sort (port->lines) string<?))

invocabile da riga di comando con:

racket -e '(for-each displayln (sort (port->lines) string<?))'

NoteModifica

  1. ^ Index
  2. ^ A Handful of Useful awk One-Liners
  3. ^ In ghci si può usare direttamente un identificatore come Data.List.sort, mentre in un eseguibile standalone è necessario eseguire l'import di Data.List.
  4. ^ pyp
  5. ^ Pyline

Voci correlateModifica

Collegamenti esterniModifica

  Portale Informatica: accedi alle voci di Wikipedia che trattano di informatica