Suggerimenti per giocare a golf in Clean


17

Quali consigli generali hai per giocare a golf in Clean? Si prega di pubblicare solo idee che possono essere applicate ai problemi del codice golf in generale e che sono almeno in qualche modo specifiche per Clean.

Se non hai mai sentito parlare di Clean, puoi scoprire di più qui .
In alternativa, puoi unirti alla chat room .

Risposte:


10

Evitare import StdEnvquando possibile

Per l'accesso funzioni built-in, anche quelli apparentemente di base come (==)o map, è necessaria una dichiarazione di importazione, di solito import StdEnvperché importa i moduli più comuni, come StdInt, StdBoole così via (vedi qui per più informazioni StdEnv).

Tuttavia, è possibile evitare questa importazione per alcune sfide e utilizzare solo le funzionalità del linguaggio principale come la comprensione dell'elenco e la corrispondenza dei modelli.

Ad esempio, invece di

import StdEnv 
map f list

si può scrivere

[f x\\x<-list]

Elenco di alternative:

Alcune funzioni o invocazioni di funzioni che richiedono import StdEnv, un'alternativa che non necessita dell'importazione e una stima approssimativa dei byte salvati.

  • hd-> (\[h:_]=h), ~ 6 byte
  • tl-> (\[_:t]=t), ~ 6 byte
  • map f list-> [f x\\x<-list], ~ 10 byte
  • filter p list-> [x\\x<-list|p x], ~ 11 byte
  • (&&)-> %a b|a=b=a;%, ~ 6 byte
  • (||)-> %a b|a=a=b;%, ~ 6 byte
  • not-> %a|a=False=True;%, ~ 1 byte
  • and-> %[a:r]|a= %r=a;%_=True, ~ 0 byte
  • or-> %[a:r]|a=a= %r;%_=False, ~ 0 byte

È improbabile che gli ultimi pochi salvino effettivamente i byte, poiché una sostituzione diretta produce più byte rispetto all'importazione, ma potrebbe essere possibile nei casi in cui è comunque necessaria la ricorsione sull'elenco.

Questo suggerimento è stato usato con successo qui .


Non è import StdEnv+ a and b(21 byte) minore %[a:r]|a= %r=a;%_=True(22 byte), però? O sarebbe import StdEnv+ a=True and b=True(31 byte), nel qual caso è davvero più breve? (Non ho mai programmato in Clean, tra.)
Kevin Cruijssen il

@KevinCruijssen Ne stavamo solo discutendo in chat . È vero che è improbabile che quelli salvino byte, tranne forse quando il programma deve comunque ricorrere a un elenco.
Laikoni,

4
Ah ok. Forse potrebbe anche essere utile indicare quanti byte vengono salvati con l'alternativa (ovvero map f list -> [f x\\x<-list] (11 bytes saved)(o qualcosa di simile)).
Kevin Cruijssen,

@KevinCruijssen Fatto.
Laikoni,

5

Sapere come imparare la lingua

Dopo tutto, come può chiunque giocare a golf in una lingua che non può usare!

in linea

Clean non è un linguaggio noto o ben documentato, e il nome certamente non rende facile trovare le risorse necessarie per rimediare a questi problemi ... o no?

Clean era originariamente chiamato Concurrent Clean , che è ancora usato nella prefazione di quasi tutti i documenti relativi a Clean, quindi se stai cercando Clean, cerca invece Concurrent Clean.

Una delle somiglianze più notevoli di Clean con Haskell (di cui ce ne sono molte) è l'esistenza di Cloogle , che è un motore di ricerca di funzioni che copre le biblioteche con cui viene spedito Clean.

localmente

Le librerie fornite da Clean sono sotto forma di file di origine Clean decentemente commentati, in qualche modo autocompattanti, che possono essere sfogliati utilizzando l'IDE.
(Viene fornito anche con programmi di esempio completi, sotto $INSTALL/Examples.)

A proposito, la versione di Windows di Clean è dotata di un IDE - sebbene sia abbastanza limitata dagli standard moderni, è migliore dei mondi rispetto all'utilizzo di un editor di testo e della riga di comando.
Le due caratteristiche più utili (nel contesto dell'apprendimento) sono:

  • Puoi fare doppio clic su un errore per vedere su quale linea si trova
  • È possibile evidenziare il nome di un modulo e premere [Ctrl]+[D]per aprire il file di definizione (o utilizzare [Ctrl]+[I]per il file di implementazione) e alternare tra la definizione e il file di implementazione con[Ctrl]+[/]

4

Dimentica la codifica dei caratteri

Il compilatore di Clean non si preoccupa di quale codifica pensi di aver salvato il file di origine, ma solo dei valori di byte nel file. Questo ha alcune conseguenze chiare.

Nel corpo del codice sorgente sono consentiti solo byte con punti di codice corrispondenti ai caratteri ASCII stampabili, oltre a quelli per \t\r\n.

letterali:

In Stringe [Char]letterali ( "stuff"e ['stuff']rispettivamente), sono consentiti tutti i byte eccetto 0 , con l'avvertenza che "e 'deve essere evasa (per Stringe [Char]rispettivamente), e che i ritorni a capo e i carrelli devono essere sostituiti con \ne \r(anche rispettivamente).

In Charletterale, è consentito qualsiasi byte tranne 0 , nel senso che:

'\n'

'
'

Sono uguali, ma il secondo è più corto di un byte.

Escaping:

A parte le lettere di \t\r\nescape standard (ecc.), Tutte le sequenze di escape non numeriche in Clean sono o per la barra o per la citazione utilizzata per delimitare il valore letterale in cui è presente la escape.

Per le sequenze di escape numeriche, il numero viene trattato come un valore ottale terminato dopo tre cifre. Ciò significa che se si desidera un null seguito dal carattere 1in a String, è necessario utilizzare "\0001"(o "\0\61") e non "\01" . Tuttavia, se segui la fuga con qualsiasi cosa tranne i numeri, puoi omettere gli zeri iniziali.

conseguenze:

Questa stranezza con il modo in cui Clean gestisce i suoi file sorgente consente Stringe ['Char']diventa efficacemente sequenze di numeri a una cifra di base 256 - che ha una moltitudine di usi per il golf di codice, come la memorizzazione di indici (fino a 255, ovviamente).


3

Il nome funziona con simboli

Quando si definisce una funzione, è spesso più breve utilizzare una combinazione di !@$%^&*~-+=<:|>.?/\rispetto all'uso di caratteri alfanumerici, poiché consente di omettere gli spazi bianchi tra gli identificatori.

Ad esempio: ?a=a^2è più breve di f a=a^2e anche invocarlo è più breve.

Tuttavia :

Se l'identificatore di funzione viene utilizzato accanto ad altri simboli, che possono combinarsi per formare un identificatore diverso, ma valido , verranno tutti analizzati come un identificatore e verrà visualizzato un errore.

Ad esempio: ?a+?banalizza come? a +? b

Inoltre:

E 'possibile sovrascrivere identificatori importate in Clean, e così gli unici identificatori simbolo singolo carattere che non sono già utilizzati in StdEnvsono @$?. La sovrascrittura ^-+(ecc.) Può essere utile se hai bisogno di più identificatori simbolici, ma fai attenzione a non sovrascrivere quello che stai utilizzando.


3

Conosci i tuoi nodi K

Alcuni dei costrutti più forti (per il golf) in linguaggi funzionali sono let ... in ....
Pulito ovviamente, ha questo e qualcosa di meglio - il #.

Cos'è un nodo?

Clean #, e l'onnipresente |(pattern guard) sono entrambi noti come "espressioni di nodo".
In particolare, essi consentono di programmare imperatively- ish in Clean (che è veramente buono qui!).

Il #(let-before):

Entrambi calcolano il valore di un numero intero dato come stringa, moltiplicato per la somma dei suoi caratteri

f s=let i=toInt s;n=sum[toInt c\\c<-:s]in n*i

f s#i=toInt s
#s=sum[toInt c\\c<-:s]
=s*i

Nota come la versione con #è più corta e come possiamo ridefinirla s. Ciò è utile se non abbiamo bisogno del valore che una variabile ha quando la riceviamo, quindi possiamo semplicemente riutilizzare il nome. ( letpuò incorrere in problemi quando lo fai)

Ma usare letè più facile quando hai bisogno di qualcosa del genereflip f = let g x y = f y x in g

La |(protezione del modello):

La protezione del modello di Clean può essere utilizzata come quella di molti altri linguaggi funzionali, tuttavia può anche essere usata come un imperativo if ... else .... E una versione più breve dell'espressione ternaria.

Ad esempio, tutti restituiscono il segno di un numero intero:

s n|n<>0|n>0=1= -1
=0

s n=if(n<>0)if(n>0)1(-1)0

s n|n>0=1|n<0= -1=0

Ovviamente, l'ultimo che usa la guardia più tradizionalmente è il più breve, ma il primo mostra che puoi annidarli (ma solo due clausole di ritorno incondizionate possono apparire sulla stessa riga nella regola di layout), e il secondo mostra quali il primo lo fa logicamente.

Una nota:

Puoi usare queste espressioni praticamente ovunque. In lambda, case ... of, let ... in, etc.


1

Se stai usando un Stringdovresti usareText

La conversione in stringhe e la manipolazione di stringhe (il {#Char}/ Stringtipo, non il [Char]tipo) è piuttosto lunga e dannosa per il golf. Il Textmodulo risolve questo problema.

Conversione:

Textdefinisce l'operatore <+per due tipi che hanno toStringdefinito.
Questo operatore, usato a<+ballo stesso modo di toString a+++toString b- salva almeno 19 byte . Anche se includi l'importazione extra ,Texte la usi solo una volta, salva comunque 14 byte!

Manipolazione:

Textdefinisce alcuni punti di manipolazione delle stringhe mancanti da StdEnv:

  • L'operatore +per le stringhe, che è molto più corto di +++(da StdEnv)
  • indexOf, con il comportamento di tipo C di restituire -1anziché in caso di Nothingfallimento
  • concat, che concatena un elenco di stringhe
  • join, che unisce un elenco di stringhe utilizzando una stringa di separazione
  • split, che divide una stringa in un elenco di stringhe su una sottostringa

1

Usa lambda più brevi

A volte ti ritrovi a usare un'espressione lambda (per passare a map, o sortBy, ecc.). Quando lo fai (scrivendo lambda), ci sono molti modi in cui puoi farlo.

Il modo giusto:

Questo è sortBy, con un idiomatico lambda elenchi di ordinamento dal più lungo al più breve

sortBy (\a b = length a > length b)

L'altro modo giusto:

Se stai usando Data.Func, puoi anche farlo

sortBy (on (>) length)

La via breve:

Questa è la stessa cosa, ma con una sintassi più golfista

sortBy(\a b=length a>length b)

L'altro modo:

Utilizzando la composizione non inferiore è questa volta, ma può essere più brevi volte

sortBy(\a=(>)(length a)o length)

L'altro modo:

Mentre è un po 'inventato qui, puoi usare le protezioni in lambdas

sortBy(\a b|length a>length b=True=False)

E anche espressioni di nodi let-before

sortBy(\a b#l=length
=l a>l b)

Una nota:

Esistono altre due forme di lambda (\a b . ...)e (\a b -> ...), la seconda delle quali è identica alla =variante, e la prima esiste per qualche motivo e spesso sembra che tu stia provando ad accedere a una proprietà di qualcosa invece di definire una lambda, quindi don non usarlo.


1
Dopo aver visto alcuni dei tuoi programmi di golf, ho avuto l'impressione che \a=... fosse la solita sintassi lambda di Clean: P
Ørjan Johansen,

Puoi anche aggiungere le protezioni in lambda, come usato qui . Questo non è documentato (contraddice persino il rapporto sulla lingua), ma funziona. Inoltre, ->e =per lambda sono identici per quanto riguarda il compilatore ( ->è la vecchia sintassi). Solo .è diverso (ma non so esattamente come).

E in questo esempio particolare potresti prendere in considerazione l'utilizzo on(<)length, anche se l' Data.Funcimportazione ti interromperà a meno che non ne abbia già bisogno.

@Keelan Cool. Lo aggiornerò più tardi oggi. Penso che puoi anche usare let-before ( #) in lambdas.
Οuroso

Sì, puoi :-)

0

Usa i letterali dell'elenco dei personaggi

Un elenco di caratteri letterale è un modo abbreviato di scrivere qualcosa di simile ['h','e','l','l','o']a ['hello'].

Questo non è il limite della notazione, ad esempio:

  • repeat'c'diventa ['c','c'..]diventa['cc'..]
  • ['z','y'..'a'] diventa ['zy'..'a']
  • ['beginning']++[a,b,c]++['end'] diventa ['beginning',a,b,c,'end']
  • ['prefix']++suffix diventa ['prefix':suffix]

Anche questi funzionano in abbinamento:

  • ['I don't care about the next character',_,'but I do care about these ones!']

0

A volte codeè più breve

Clean ha un sacco di funzioni davvero utili nelle librerie standard, alcune delle quali sono incredibilmente dettagliate da usare senza accesso *Worlde l'uso *Worldin code-golf è generalmente una cattiva idea comunque.

Per ovviare a questo problema, ci sono spesso ccalls che puoi usare all'interno dei codeblocchi.

Qualche esempio:

Ora di sistema

import System.Time,System._Unsafe
t=toInt(accUnsafe(time))

Quanto sopra è di 58 byte, ma è possibile salvare 17 byte (fino a 40 + 1) con:

t::!Int->Int
t _=code{ccall time "I:I"
}

Numeri casuali

Questo non salva i byte da solo, ma evita di dover passare da un elenco a genRandInt

s::!Int->Int
s _=code{ccall time "I:I"ccall srand "I:I"
}
r::!Int->Int
r _=code{ccall rand "I:I"
}

Altri usi

Oltre a questi due, che sono probabilmente gli usi principali per questo nel code-golf, è possibile chiamare qualsiasi funzione denominata (incluso ma non limitato a tutte le scale di sistema), incorporare un assembly arbitrario instruction <byte>e incorporare il codice per la macchina ABC.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.