Tipps zum Golfen in Clean

17

Welche allgemeinen Tipps haben Sie zum Golfen in Clean? Bitte posten Sie nur Ideen, die generell auf Code-Golfprobleme angewendet werden können und die zumindest etwas spezifisch für Clean sind.

Wenn Sie noch nie von Clean gehört haben, finden Sie hier weitere Informationen .
Oder Sie können dem Chatroom beitreten .

Οurous
quelle

Antworten:

10

Vermeiden Sie import StdEnvwenn möglich

Um den Zugriff über integrierte Funktionen, auch scheinbar grundlegend diejenigen wie (==)oder mapwird eine Import - Anweisung benötigt, in der Regel , import StdEnvweil sie die am häufigsten verwendeten Module wie importiert StdInt, StdBoolund so weiter (siehe hier für weitere Informationen auf StdEnv).

Es kann jedoch möglich sein, diesen Import für einige Herausforderungen zu vermeiden und nur die Kernsprachfunktionen wie Listenverständnis und Mustervergleich zu verwenden.

Zum Beispiel anstelle von

import StdEnv 
map f list

man kann schreiben

[f x\\x<-list]

Liste der Alternativen:

Einige Funktionen oder Funktionsaufrufe, die benötigt werden import StdEnv, eine Alternative, die keinen Import benötigt, und eine grobe Schätzung der gespeicherten Bytes.

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

Es ist unwahrscheinlich, dass die letzten Bytes tatsächlich gespeichert werden, da eine direkte Ersetzung mehr Bytes als der Import ergibt. Dies kann jedoch in Fällen möglich sein, in denen die Rekursion über die Liste ohnehin erforderlich ist.

Dieser Tipp erfolgreich verwendet hier .

Laikoni
quelle
Ist import StdEnv+ a and b(21 Bytes) nicht kleiner als %[a:r]|a= %r=a;%_=True(22 Bytes)? Oder wäre es import StdEnv+ a=True and b=True(31 Bytes), in welchem ​​Fall ist es tatsächlich definitiv kürzer? (Ich habe übrigens noch nie in Clean programmiert.)
Kevin Cruijssen
@ KevinCruijssen Wir haben das gerade im Chat besprochen . Es ist wahr, dass diese wahrscheinlich keine Bytes speichern, es sei denn, das Programm muss sich trotzdem über eine Liste informieren.
Laikoni
4
Ach ok Möglicherweise kann es auch nützlich sein, anzugeben, wie viele Bytes mit der Alternative gespeichert werden (dh map f list -> [f x\\x<-list] (11 bytes saved)(oder etwas Ähnliches)).
Kevin Cruijssen
@ KevinCruijssen Fertig.
Laikoni
5

Wissen, wie man die Sprache lernt

Wie kann man in einer Sprache Golf spielen, die man nicht beherrscht?

Online

Clean ist keine bekannte oder gut dokumentierte Sprache, und der Name macht es sicherlich nicht leicht, dringend benötigte Ressourcen zu finden, um diese Probleme zu beheben ... oder doch?

"Bereinigen" hieß ursprünglich " Concurrent Clean" , das immer noch in fast allen Dokumenten zum Thema "Bereinigen" verwendet wird. Wenn Sie also nach "Bereinigen" suchen, suchen Sie stattdessen nach "Concurrent Clean".

Eine der bemerkenswertesten Ähnlichkeiten von Clean mit Haskell (von denen es viele gibt) ist die Existenz von Cloogle , einer Funktionssuchmaschine, die die Bibliotheken abdeckt , mit denen Clean ausgeliefert wird.

Örtlich

Die Bibliotheken, mit denen Clean ausgeliefert wird, bestehen aus anständig kommentierten, etwas selbstdokumentierenden Clean-Quelldateien, die mithilfe der IDE durchsucht werden können.
(Es kommt auch mit vollständigen Beispielprogrammen, unter $INSTALL/Examples.)

Apropos, die Windows-Version von Clean wird mit einer IDE geliefert - obwohl sie für moderne Standards ziemlich begrenzt ist, ist sie um Welten besser als die Verwendung eines Texteditors und der Befehlszeile.
Die zwei nützlichsten Funktionen (im Kontext des Lernens) sind:

  • Sie können auf einen Fehler doppelklicken, um zu sehen, in welcher Zeile er sich befindet
  • Sie können einen Modulnamen markieren und drücken [Ctrl]+[D], um die Definitionsdatei zu öffnen (oder [Ctrl]+[I]für die Implementierungsdatei zu verwenden) und zwischen der Definitions- und Implementierungsdatei mit umzuschalten[Ctrl]+[/]
Οurous
quelle
4

Vergessen Sie die Zeichenkodierung

Der Compiler von Clean kümmert sich nicht darum, unter welcher Codierung Sie die Quelldatei Ihrer Meinung nach gespeichert haben, sondern nur um die Byte-Werte in der Datei. Dies hat einige nette Konsequenzen.

Im Hauptteil des Quellcodes sind nur Bytes mit Codepunkten zulässig, die den druckbaren ASCII-Zeichen entsprechen, zusätzlich zu denen für \t\r\n.

Literale:

In Stringund [Char]Literale ( "stuff"und ['stuff']jeweils), irgendwelche Bytes außer 0 erlaubt sind, mit dem Vorbehalt , dass , "und 'müssen (für maskiert werden Stringund [Char]jeweils), und dass die Zeilenumbrüche und carraige kehren muss ersetzt werden , \nund \r(ebenfalls jeweils).

In CharLiteralen ist jedes Byte außer 0 zulässig, was bedeutet, dass:

'\n'

'
'

Sind gleich, aber die Sekunde ist ein Byte kürzer.

Flucht:

Abgesehen von den Standard-Escapezeichen \t\r\n(usw.) stehen alle nicht numerischen Escapezeichenfolgen in Clean entweder für den Schrägstrich oder für das Anführungszeichen, das zur Begrenzung des Literal verwendet wird, in dem sich das Escapezeichen befindet.

Bei numerischen Escape-Sequenzen wird die Zahl als Oktalwert behandelt, der nach drei Ziffern endet. Dies bedeutet , dass , wenn Sie eine Null gefolgt von dem Zeichen wollen 1in ein String, müssen Sie verwenden "\0001"(oder "\0\61") und nicht "\01" . Wenn Sie dem Escape jedoch nur Zahlen folgen , können Sie die führenden Nullen weglassen.

Folgen:

Diese Marotte, wie sauber behandelt seine Quelldateien ermöglicht Stringund ['Char']effektiv wurden Sequenzen von Basis-256 mit einem einstelligen Zahlen - die eine Vielzahl von Anwendungen für die Code-Golf haben, wie zum Beispiel der Speicherung Indizes (bis zu 255, natürlich).

Οurous
quelle
3

Namensfunktionen mit Symbolen

Beim Definieren einer Funktion ist es oftmals kürzer, eine Kombination aus !@$%^&*~-+=<:|>.?/\als alphanumerischen Zeichen zu verwenden, da Sie Leerzeichen zwischen Bezeichnern weglassen können.

Zum Beispiel: ?a=a^2ist kürzer als f a=a^2und das Aufrufen ist auch kürzer.

Jedoch :

Wenn der Funktionsbezeichner neben anderen Symbolen verwendet wird, die zu einem anderen, aber gültigen Bezeichner kombiniert werden können , werden sie alle als ein Bezeichner analysiert, und es wird ein Fehler angezeigt.

Zum Beispiel: ?a+?banalysiert als? a +? b

Zusätzlich:

Es ist möglich , importierte Identifikatoren in Rein zu überschreiben, so dass die nur mit einem Zeichen Symbol Kennungen , die nicht bereits verwendet werden StdEnvsind @$?. Überschreiben ^-+(usw.) kann nützlich sein, wenn Sie mehr symbolische Bezeichner benötigen, aber achten Sie darauf, dass Sie keine von Ihnen verwendeten überschreiben.

Οurous
quelle
3

Kennen Sie Ihre K- Knoten

Einige der stärksten Konstrukte (zum Golfen) in funktionalen Sprachen sind let ... in ....
Sauber hat das natürlich auch was besseres - das #.

Was ist ein Knoten?

Clean's #und das allgegenwärtige |(Pattern Guard) werden als 'Node Expressions' bezeichnet.
Bemerkenswert ist , sie ermöglichen es Ihnen imperatively- zu programmieren ish in Clean (was hier wirklich gut ist!).

Die #(let-before):

Beide berechnen den Wert einer Ganzzahl, die als Zeichenfolge angegeben wird, multipliziert mit der Summe ihrer Zeichen

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

Beachten Sie, wie die Version mit #kürzer ist und wie wir sie neu definieren können s. Dies ist nützlich, wenn wir den Wert, den eine Variable beim Empfang hat, nicht benötigen, sodass wir den Namen einfach wiederverwenden können. ( letkann dabei auf Probleme stoßen)

Die Verwendung letist jedoch einfacher, wenn Sie etwas benötigenflip f = let g x y = f y x in g

Die |(Musterwache):

Der Pattern Guard von Clean kann wie in vielen anderen funktionalen Sprachen verwendet werden - er kann jedoch auch wie ein Imperativ verwendet werden if ... else .... Und eine kürzere Version des ternären Ausdrucks.

Diese geben beispielsweise alle das Vorzeichen einer Ganzzahl zurück:

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

Natürlich ist die letzte, die den Guard herkömmlicher verwendet, die kürzeste, aber die erste zeigt, dass Sie sie verschachteln können (in der Layoutregel können jedoch nur zwei bedingungslose Rückgabeklauseln in derselben Zeile stehen), und die zweite zeigt, was der Guard verwendet zuerst logisch.

Eine Notiz:

Sie können diese Ausdrücke grundsätzlich überall verwenden. In lambda, case ... of, let ... inetc.

Οurous
quelle
1

Wenn Sie eine verwenden String, sollten Sie verwendenText

Die Umwandlung in Saiten und die Manipulation von Saiten (die {#Char}/ StringArt, nicht die [Char]Art) ist ziemlich langwierig und schlecht für das Golfen. Das TextModul behebt dieses Problem.

Umwandlung:

TextDefiniert den Operator <+für zwei beliebige Typen, die toStringdefiniert wurden.
Dieser Operator wird wie a<+bfolgt verwendet: toString a+++toString b- Spart mindestens 19 Byte . Auch wenn Sie den zusätzlichen Import einschließen ,Textund ihn nur einmal verwenden, werden immer noch 14 Byte gespart!

Manipulation:

TextDefiniert einige String-Manipulations-Heftklammern, die fehlen in StdEnv:

  • Der Operator +für Strings, der viel kürzer ist als +++(von StdEnv)
  • indexOf, mit dem C-ähnlichen Verhalten, zurückzukehren, -1anstatt Nothingbei einem Fehler
  • concat, die eine Liste von Zeichenfolgen verkettet
  • join, der eine Liste von Zeichenfolgen mit einer Trennzeichenfolge verknüpft
  • split, der einen String in eine Liste von Strings auf einem Teilstring aufteilt
Οurous
quelle
1

Verwenden Sie kürzere Lambdas

Manchmal verwenden Sie einen Lambda-Ausdruck (zum Weitergeben an mapoder sortByusw.). Wenn Sie dies tun (Lambdas schreiben), gibt es eine Reihe von Möglichkeiten, wie Sie dies tun können.

Der richtige Weg:

Dies ist sortBymit einer idiomatischen Lambda-Sortierliste von der längsten zur kürzesten

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

Der andere richtige Weg:

Wenn Sie verwenden Data.Func, können Sie auch tun

sortBy (on (>) length)

Der kurze Weg:

Das ist das Gleiche, aber mit einer golferen Syntax

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

Der andere Weg:

Die Komposition ist diesmal nicht kürzer, kann aber manchmal kürzer sein

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

Der andere andere Weg:

Während es hier ein bisschen erfunden ist, können Sie Wachen in Lambdas verwenden

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

Und auch Let-Before-Node-Ausdrücke

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

Eine Notiz:

Es gibt zwei weitere Formen von Lambda, (\a b . ...)und (\a b -> ...)die letztere ist identisch mit der =Variante, und die erstere existiert aus irgendeinem Grund und sieht oft so aus, als ob Sie versuchen, auf eine Eigenschaft von etwas zuzugreifen, anstatt ein Lambda zu definieren benutze es nicht.

Οurous
quelle
1
Nachdem ich einige Ihrer Golfprogramme gesehen hatte, hatte ich den Eindruck, \a=... es handele sich um die übliche Lambda-Syntax von Clean: P
Ørjan Johansen
Sie könnten auch die Wachen in Lambda hinzufügen, wie verwendet hier . Dies ist undokumentiert (es widerspricht sogar dem Sprachbericht), funktioniert aber. Auch ->und =für Lambdas sind identisch, was den Compiler betrifft ( ->ist alte Syntax). Nur .ist anders (aber ich weiß nicht genau wie).
Und in diesem speziellen Beispiel könnten Sie die Verwendung in Betracht ziehen on(<)length, obwohl der Data.FuncImport Sie trennen wird, es sei denn, Sie benötigen ihn bereits.
@ Keelan Cool. Ich werde dies heute später aktualisieren. Ich denke, Sie können let-before ( #) auch in Lambdas verwenden.
Freitag,
Ja, das können Sie :-)
0

Verwenden Sie Zeichenlistenliterale

Ein Zeichenlisten-Literal ist eine Abkürzung für ['h','e','l','l','o']as ['hello'].

Dies ist nicht die Grenze der Notation, zum Beispiel:

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

Diese funktionieren auch im Matching:

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

Manchmal codeist kürzer

Clean verfügt über eine Reihe wirklich nützlicher Funktionen in den Standardbibliotheken, von denen einige unglaublich ausführlich sind, ohne auf sie zuzugreifen *World, und die Verwendung *Worldin Code-Golf ist im Allgemeinen sowieso eine schlechte Idee.

Um dieses Problem zu umgehen, gibt es häufig ccalls, die Sie codestattdessen in Blöcken verwenden können.

Einige Beispiele:

Systemzeit

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

Das obige sind 58 Bytes, aber Sie können 17 Bytes (bis zu 40 + 1) sparen mit:

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

Zufällige Zahlen

Dieser speichert keine Bytes für sich, sondern vermeidet das Umgehen einer Liste von genRandInt

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

Andere Verwendungen

Zusätzlich zu diesen beiden Funktionen, die wahrscheinlich die Hauptverwendung für Code-Golf sind, können Sie eine beliebige benannte Funktion aufrufen (einschließlich, aber nicht beschränkt auf jeden Systemaufruf), eine beliebige Assembly mit instruction <byte>einbetten und Code für die ABC-Maschine einbetten.

Οurous
quelle