Wskazówki do gry w golfa w Clean

17

Jakie masz ogólne wskazówki na temat gry w golfa w Clean? Proszę zamieszczać tylko pomysły, które można zastosować do ogólnych problemów z golfem i są przynajmniej nieco specyficzne dla Clean.

Jeśli nigdy nie słyszałeś o Clean, możesz dowiedzieć się więcej tutaj .
Lub możesz dołączyć do pokoju czatu .

Obrzydliwe
źródło

Odpowiedzi:

10

Unikaj, import StdEnvkiedy to możliwe

Aby uzyskać dostęp do funkcji wbudowanych, nawet pozornie podstawowe, jak (==)i map, potrzebne jest oświadczenie import, zwykle import StdEnvdlatego, że importuje najczęstsze moduły jak StdInt, StdBooli tak dalej (patrz tutaj , aby uzyskać więcej informacji na temat StdEnv).

Można jednak uniknąć tego importu w przypadku niektórych wyzwań i po prostu użyć podstawowych funkcji języka, takich jak listy i dopasowanie wzorca.

Na przykład zamiast

import StdEnv 
map f list

można pisać

[f x\\x<-list]

Lista alternatyw:

Niektóre funkcje lub wywołania funkcji, które wymagają import StdEnv, alternatywa, która nie wymaga importu i przybliżone oszacowanie zapisanych bajtów.

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

Kilka ostatnich raczej nie zapisuje bajtów, ponieważ bezpośrednia zamiana daje więcej bajtów niż import, ale może to być możliwe w przypadkach, gdy i tak potrzebna jest rekurencja na liście.

Ta wskazówka została z powodzeniem wykorzystana tutaj .

Laikoni
źródło
Czy jednak import StdEnv+ a and b(21 bajtów) nie jest mniejszy niż %[a:r]|a= %r=a;%_=True(22 bajtów)? A może byłby to import StdEnv+ a=True and b=True(31 bajtów), w którym to przypadku rzeczywiście jest zdecydowanie krótszy? (Nigdy nie programowałem w Clean, btw.)
Kevin Cruijssen
@KevinCruijssen Właśnie rozmawialiśmy o tym na czacie . Prawdą jest, że zapisywanie tych bajtów jest mało prawdopodobne, z wyjątkiem sytuacji, gdy program i tak musi ponownie utworzyć listę.
Laikoni
4
Ach ok. Być może warto również określić, ile bajtów jest zapisanych z alternatywą (tj. map f list -> [f x\\x<-list] (11 bytes saved)(Lub czymś podobnym)).
Kevin Cruijssen
@KevinCruijssen Gotowe.
Laikoni
5

Umieć uczyć się języka

W końcu, jak każdy może grać w golfa w języku, którego nie może używać!

online

Czysty nie jest dobrze znanym ani dobrze udokumentowanym językiem, a nazwa z pewnością nie ułatwia znalezienia potrzebnych zasobów, aby rozwiązać te problemy ... czy to prawda?

Clean pierwotnie nazywał się Concurrent Clean , który jest nadal używany we wstępie niemal każdego dokumentu związanego z Clean - więc jeśli szukasz Clean, poszukaj Concurrent Clean.

Jednym z bardziej niezwykłych podobieństw Clean do Haskell (których jest wiele) jest istnienie Cloogle , który jest wyszukiwarką funkcji obejmującą biblioteki, z którymi czyści statki.

Lokalnie

Biblioteki, które są dostarczane z programem Clean, mają postać odpowiednio skomentowanych, nieco samo dokumentujących się plików źródłowych Clean, które można przeglądać za pomocą IDE.
(Zawiera również pełne przykładowe programy, poniżej $INSTALL/Examples.)

Mówiąc o tym, wersja Clean systemu Windows jest wyposażona w IDE - chociaż jest dość ograniczona nowoczesnymi standardami, jest lepsza na świecie niż przy użyciu edytora tekstu i wiersza poleceń.
Dwie najbardziej przydatne funkcje (w kontekście uczenia się) to:

  • Możesz kliknąć dwukrotnie błąd, aby zobaczyć, w której linii się znajduje
  • Możesz podświetlić nazwę modułu i nacisnąć, [Ctrl]+[D]aby otworzyć plik definicji (lub użyć [Ctrl]+[I]do pliku implementacji), i przełączać się między plikiem definicji a plikiem implementacji za pomocą[Ctrl]+[/]
Obrzydliwe
źródło
4

Zapomnij o kodowaniu znaków

Kompilator Clean nie dba o to, jakie kodowanie zapisałeś jako plik źródłowy, tylko o wartości bajtów w pliku. Ma to pewne konsekwencje.

W treści kodu źródłowego dozwolone są tylko bajty z punktami kodowymi odpowiadającymi drukowalnym znakom ASCII, oprócz tych dla \t\r\n.

Literały:

W Stringi [Char]literały ( "stuff"i ['stuff']odpowiednio), wszelkie bajtów z wyjątkiem 0 są dozwolone, z zastrzeżeniem, że "i 'musi być uciekł (dla Stringi [Char]odpowiednio) oraz, że nowe linie i carraige powroty muszą być zastąpione \ni \r(również odpowiednio).

W Charliterałach dozwolony jest dowolny bajt oprócz 0 , co oznacza, że:

'\n'

'
'

Są takie same, ale drugi jest krótszy o jeden bajt.

Ucieczka:

Inne niż standardowe znaki specjalne \t\r\n(itp.), Wszystkie nieliczbowe sekwencje specjalne w Clean są albo dla ukośnika, albo dla cudzysłowu używanego do określenia literału, w którym znajduje się ucieczka.

W przypadku numerycznych sekwencji ucieczkowych liczba jest traktowana jako wartość ósemkowa zakończona trzema cyframi. Oznacza to, że jeśli chcesz, aby null poprzedził znak 1w String, musisz użyć "\0001"(lub "\0\61"), a nie "\01" . Jeśli jednak podążasz za ucieczką, używając liczb innych niż cyfry, możesz pominąć początkowe zera.

Konsekwencje:

Dziwactwo tego, w jaki sposób Clean obsługuje swoje pliki źródłowe, pozwala Stringi ['Char']skutecznie staje się ciągiem podstawowych 256 cyfr jednocyfrowych - co ma wiele zastosowań w golfie kodowym, takich jak przechowywanie indeksów (oczywiście do 255).

Obrzydliwe
źródło
3

Nazwy funkcji z symbolami

Podczas definiowania funkcji często krótsze jest użycie kombinacji !@$%^&*~-+=<:|>.?/\niż znaków alfanumerycznych, ponieważ pozwala to pominąć białe znaki między identyfikatorami.

Na przykład: ?a=a^2jest krótszy niż f a=a^2, a wywoływanie go również jest krótsze.

Jednak :

Jeśli identyfikator funkcji jest używany obok innych symboli, które można łączyć w celu utworzenia innego, ale poprawnego identyfikatora, wszystkie zostaną przeanalizowane jako jeden identyfikator, a zobaczysz błąd.

Na przykład: ?a+?bparsuje jako? a +? b

Do tego:

Możliwe jest zastąpienie zaimportowanych identyfikatorów w Clean, więc jedynymi jednoznakowymi identyfikatorami symboli, które nie są jeszcze używane, StdEnv@$?. Nadpisywanie ^-+(itp.) Może być przydatne, jeśli potrzebujesz więcej symbolicznych identyfikatorów, ale uważaj, aby nie nadpisać jednego, którego używasz.

Obrzydliwe
źródło
3

Poznaj swoje węzły K.

Niektóre z najsilniejszych konstrukcji (do gry w golfa) w językach funkcjonalnych to let ... in ....
Oczyścić, oczywiście, ma to i coś lepszego - #.

Co to jest węzeł?

Zarówno Clean #, jak i wszechobecny |(ochrona wzorca) są znane jako „wyrażenia węzłów”.
Warto zauważyć, że pozwalają na zaprogramowanie imperatively- owski w czysty (co jest naprawdę dobre tutaj!).

#(Let-wcześniej):

Oba obliczają wartość liczby całkowitej podanej jako ciąg, pomnożonej przez sumę jej znaków

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

Zwróć uwagę, że wersja z #jest krótsza i jak możemy przedefiniować s. Jest to przydatne, jeśli nie potrzebujemy wartości, którą ma zmienna, gdy ją otrzymujemy, więc możemy po prostu ponownie użyć nazwy. ( letmoże to powodować problemy)

Ale korzystanie letjest łatwiejsze, gdy potrzebujesz czegoś takiegoflip f = let g x y = f y x in g

|(Strażnik wzorzec):

Ochrona wzoru Clean może być używana tak jak w wielu innych językach funkcjonalnych - jednak może być również używana jako imperatyw if ... else .... I krótsza wersja wyrażenia potrójnego.

Na przykład wszystkie zwracają znak liczby całkowitej:

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

Oczywiście ostatni, który tradycyjnie używa wartownika, jest najkrótszy, ale pierwszy pokazuje, że można je zagnieżdżać (ale tylko dwie bezwarunkowe klauzule powrotu mogą pojawiać się w tej samej linii w regule układu), a drugi pokazuje, co pierwszy robi to logicznie.

Notka:

Możesz używać tych wyrażeń w zasadzie wszędzie. W lambdas, case ... of, let ... in, itd.

Obrzydliwe
źródło
1

Jeśli używasz String, powinieneś używaćText

Konwersja na łańcuchy i manipulowanie łańcuchami ( rodzaj {#Char}/ Stringrodzaj, a nie [Char]rodzaj) jest dość długie i niekorzystne dla gry w golfa. Te Textśrodki tego modułu.

Konwersja:

Textdefiniuje operator <+dla dowolnych dwóch toStringzdefiniowanych typów .
Ten operator, używany tak a<+bsamo, jak toString a+++toString b- oszczędza co najmniej 19 bajtów . Nawet jeśli dodasz dodatkowy import ,Texti użyjesz go tylko raz, nadal oszczędza 14 bajtów!

Manipulacja:

Textdefiniuje kilka brakujących zszywek do manipulowania łańcuchem StdEnv:

  • Operator +ciągów, który jest znacznie krótszy niż +++(z StdEnv)
  • indexOf, z zachowaniem przypominającym literę C powrotu -1zamiast Nothingawarii
  • concat, która łączy listę ciągów
  • join, który łączy listę ciągów za pomocą ciągu separatora
  • split, który dzieli ciąg na listę ciągów na podłańcuchu
Obrzydliwe
źródło
1

Używaj krótszych lambd

Czasami można znaleźć samodzielnie za pomocą wyrażenia lambda (aby przejść do map, lub sortBy, etc.). Kiedy to robisz (piszesz lambdy), możesz to zrobić na wiele sposobów.

Właściwy sposób:

Jest to możliwe sortBydzięki idiomatycznym listom sortującym lambda od najdłuższego do najkrótszego

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

Drugi właściwy sposób:

Jeśli używasz Data.Func, możesz to zrobić

sortBy (on (>) length)

Krótka droga:

To to samo, ale ze składnią golfisty

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

Inna droga:

Tym razem użycie kompozycji nie jest krótsze, ale czasami może być krótsze

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

Drugi inny sposób:

Chociaż jest tu nieco wymyślony, możesz używać strażników w lambdach

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

A także wyrażenia węzłów „let-before”

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

Notka:

Istnieją jeszcze dwie formy lambda, (\a b . ...)i (\a b -> ...)ta ostatnia jest identyczna z =wariantem, a ta pierwsza istnieje z jakiegoś powodu i często wygląda na to, że próbujesz uzyskać dostęp do właściwości czegoś zamiast zdefiniować lambda, więc nie nie używaj tego.

Obrzydliwe
źródło
1
Po obejrzeniu niektórych swoich golfed programów, ja mam wrażenie \a=... było składnię czysty za lambda: P
Ørjan Johansen
Możesz także dodać strażników w lambda, jak tutaj użyto . Jest to nieudokumentowane (nawet zaprzecza raportowi językowemu), ale działa. Również ->i =dla lambdas są identyczne jeśli chodzi o kompilator dotyczy ( ->jest składnia życia). Tylko .jest inny (ale nie wiem dokładnie, jak to zrobić).
I w tym konkretnym przykładzie możesz rozważyć użycie on(<)length, chociaż Data.Funcimport spowoduje uszkodzenie, chyba że już go potrzebujesz.
@Keelan Cool. Zaktualizuję to później dzisiaj. Myślę, że możesz używać let-before ( #) w lambdas.
tragiczny
Tak, możesz :-)
0

Użyj literałów z listy postaci

Dosłowne listy znaków to skrótowy sposób pisania czegoś takiego ['h','e','l','l','o']jak ['hello'].

To nie jest granica zapisu, na przykład:

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

Działa to również w dopasowywaniu:

  • ['I don't care about the next character',_,'but I do care about these ones!']
Obrzydliwe
źródło
0

Czasami codejest krótszy

Clean ma kilka naprawdę przydatnych funkcji w standardowych bibliotekach, z których niektóre są niezwykle szczegółowe, aby można było z nich korzystać bez dostępu *World, a używanie *Worldw grze w golfa jest generalnie złym pomysłem.

Aby obejść ten problem, często ccallmożna codezamiast tego użyć bloków wewnętrznych .

Kilka przykładów:

Czas systemu

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

Powyżej jest 58 bajtów, ale możesz zapisać 17 bajtów (do 40 + 1) za pomocą:

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

Losowe liczby

Ten nie zapisuje bajtów samodzielnie, ale unika konieczności przekazywania listy genRandInt

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

Inne zastosowania

Oprócz tych dwóch, które są prawdopodobnie głównymi zastosowaniami do tego w code-golfie, możesz wywołać dowolną nazwaną funkcję (w tym między innymi każde wywołanie systemowe), osadzić dowolny zestaw instruction <byte>i osadzić kod dla maszyny ABC.

Obrzydliwe
źródło