If-less programming - mini wyzwanie

1

Proponuję zabawę programistyczną bez nagród - zadaniem jest napisanie gry Tic-tac-toe (kółko i krzyżyk) bez używania wszelkiego rodzaju instrukcji warunkowych
Zadanie wymaga trochę innego myślenia niż zwykle i myślę, że może być ciekawe zwłaszcza dla początkujących programistów

Wymagania:
plansza 3x3
gracze grają naprzemiennie
komunikat w przypadku wygranej z informacją który gracz wygrał i zresetowanie gry
komunikat w przypadku zapełnienia wszystkich pól bez wygranej i zresetowanie gry

Nie można używać instrukcji if (skoków warunkowych) pod żadną postacią, w tym:

  • operatora warunkowego if ? then : else
  • wykorzystania pętli for (sprawdzanie warunku wewnątrz pętli)
  • wykorzystania pętli while (warunkowe wejście w pętlę)
  • wykorzystania null coalescing operator
  • wykorzystywanie operatorów logicznych typu || lub && do warunkowego wykonywania poleceń
  • wykorzystawania różnego rodzaju bibliotek opakowujących instrukcje warunkowe w obiekty wyższego poziomu (typu action.whenTrue(anotherAction))
  • ogólnie wszystko co można zakwalifikować jako hack do uzyskania warunkowej operacji

Przydatne wzorce:

https://pl.wikipedia.org/wiki/Pusty_Obiekt_(wzorzec_projektowy)
https://pl.wikipedia.org/wiki/Odwiedzaj%C4%85cy

Swojego rozwiązania nie umieszczam, ale jest to oczywiście możliwe
poziom master - możliwość gry z komputerem ;)

3
#include <iostream>
#include <functional>
using namespace std;

using block_t = std::function<void()>;

void _if(bool cond, block_t tru, block_t fals = []{}){
	block_t blocks[] = {
		fals,
		tru
	};
	
	blocks[cond]();
}

int main() {
	int x = 6;
	_if(x==6, []{
		cout << "Hello World!";
	}, []{
		cout << "Hail Satan!";
	});
	
	_if(x==5, []{
		cout << "Heil Hydra!";
	});
	return 0;
}

Jeżeli tablice to hack to kiepsko i w sumie masa copy paste :/ w innym wypadku tyle wystarczy, żeby pisać dalej normalnie

//EDIT: Switch nie jest zbanowany :D

0

to jak najbardziej hack
nie chodzi o to żeby znaleźć sposób na użycie if tylko żeby go nie używać wcale - nie jest w ogóle potrzebny do wykonania tego programu
i prawdopodobnie nie jest potrzebny do wykonania jakiegokolwiek programu - praca naszego mózgu to ciągły flow - nie mamy w nim tranzystorów, przerzutników ani niczego podobnego a jednak ciągły przepływ impulsów pomiędzy neuronami pozwala nam podejmować decyzje

to kiepsko i w sumie masa copy paste :/

w żadnym wypadku - pozbycie się "ifologii" zazwyczaj upiększa kod programu - chodzi o to żeby zamiast pisać warunki, podmieniać strategie i żonglować obiektami

3

Przepływ impulsów w mózgu nie jest ciągły, słabe porównanie :P
Można by wręcz powiedzieć, że w mózgu właśnie znajdują się pewne odpowiedniki instrukcji warunkowych, ponieważ nie każdy impuls "przechodzi dalej".

0

Nie jesteś w stanie wyzbyć się kompletnie warunków; Jedyne co zrobisz, to je ukryjesz (często pisząc kod dla samego pisania kodu).
Zamiast pchać na siłę OOP lepiej byłoby spróbować się z data-oriented-design i dbać o cache procesora :P

1

nie chodzi żeby się całkiem ich pozbyć - tylko pozbyć się brzydkich warunków z logiki biznesowej aplikacji
można poczytać wpisując w google if-less programming: http://antiifcampaign.com/ http://alisnic.github.io/posts/ifless/

spartanPAGE napisał(a):

Zamiast pchać na siłę OOP lepiej byłoby spróbować się z data-oriented-design i dbać o cache procesora :P

tu udowadniasz że są dwa typy programistów - Ci dla których najwyższą wartością jest czysty kod a resztę zwalają na kompilator; i ci którzy piszą ++i zamiast i++ żeby zyskać cykl zegara ;)

0

Nie jesteś w stanie wyzbyć się kompletnie warunków; Jedyne co zrobisz, to je ukryjesz (często pisząc kod dla samego pisania kodu).

Jeśli przez "warunki" masz na myśli wszelkiej maści konstrukcje języka generujące jmp-y na poziomie asemblera to jesteś w błędzie.
Patrz: https://www.cl.cam.ac.uk/~sd601/papers/mov.pdf
Cytat z pejpera:
"Yet as it turns out, we can do comparisons using only load
and store instructions. (...) "

0

Nie można używać instrukcji if (skoków warunkowych) pod żadną postacią [...]

nie chodzi żeby się całkiem ich pozbyć - tylko pozbyć się brzydkich warunków z logiki biznesowej aplikacji

Zamiast pchać na siłę OOP lepiej byłoby spróbować się z data-oriented-design i dbać o cache procesora

tu udowadniasz że są dwa typy programistów - Ci dla których najwyższą wartością jest czysty kod a resztę zwalają na kompilator; i ci którzy piszą ++i zamiast i++ żeby zyskać cykl zegara ;)

Przyznaj, nawet nie spróbowałeś się zapoznać z wymienionym terminem - bo gdybyś to zrobił to widziałbyś, ile ma to wspólnego z tematem wątku :P

2

@Mały Młot idealista z ciebie, a w praktyce to sie nie sprawdza. Bo jak nagle pojawi sie nowe wymaganie, szczególny przypadek czy coś w tym stylu to możesz:

  1. Dopisać jednego ifa
  2. Zrefaktorować pół systemu, bo żeby uwzględnic ten warunek musiałbyś przemodelować całą warstwę abstrakcji aplikacji ;]
1

Jestem idealistą ale potrafię się dostosować - wiem jak to wygląda w praktyce i często samemu dopisuję takiego ifa - nikt w komercyjnym produkcie mi nawet nie da czasu żeby to zrobić "poprawnie"
Tak czy inaczej - zadałem tylko średnio-proste zadanie do rozwiązania głównie dla początkujących chcących poćwiczyć wzorce i OOP a tu zaraz rozmowa o ideologiach ;)

spartanPAGE napisał(a):

Przyznaj, nawet nie spróbowałeś się zapoznać z wymienionym terminem - bo gdybyś to zrobił to widziałbyś, ile ma to wspólnego z tematem wątku :P

Tzn? Zadanie jest czysto teoretyczną łamigłówką wykorzystującą skrajne podejście ale wywodzi się pośrednio z tego tematu - ma mniej więcej tyle samo sensu co ograniczenie występowania cyfr w sudoku. Mimo to wydaje mi się że można wiele się nauczyć przy takich zadaniach; na samym początku ścieżki programistycznej wszystkim nam powiedziano że instrukcje warunkowe to podstawa bez której nie można stworzyć żadnego programu poza Hello world

1

Pozbycie się długich, zagnieżdżonych ifów rzeczywiście pozwala oczyścić kod, ale już dawno nigdzie nie widziałem takich potworków.

Co do zadania to masz multum języków (cała rodzina ML-i, Lispy, Swift, Rust) gdzie masz coś takiego:

match cond {
    variant1 => (),
    variant2 => (),
    _ => () /* inne przypadki */,
}

Lub podobne konstrukcje. Nie zabroniłeś tego.

Co więcej niektóre języki (Haskell, Prolog, Erlang, Elixir) oprócz tego mają jeszcze pattern matching przy wywoływaniu funkcji, więc:

predicate(true).

Wystarczy jako alternatywa dla ifa.

Wracając do zadania, to jest to niemożliwe, bo jakaś forma skoku warunkowego być musi. Nie ma innej opcji. Oczywiście możemy zabronić pisania stricte instrukcji warunkowych, ale wtedy musimy posiłkować się hackami takimi jak wyżej wymienione (tablice, pattern matching).

15
Mały Młot napisał(a)

Proponuję zabawę programistyczną bez nagród - zadaniem jest napisanie gry Tic-tac-toe (kółko i krzyżyk) bez używania wszelkiego rodzaju instrukcji warunkowych
Zadanie wymaga trochę innego myślenia niż zwykle i myślę, że może być ciekawe zwłaszcza dla początkujących programistów

Brzmi ciekawie

winerfresh napisał(a)

Wracając do zadania, to jest to niemożliwe, bo jakaś forma skoku warunkowego być musi. Nie ma innej opcji. Oczywiście możemy zabronić pisania stricte instrukcji warunkowych, ale wtedy musimy posiłkować się hackami takimi jak wyżej wymienione (tablice, pattern matching).

Niewierny Tomasz sie znalazł :P.
Ah, piątek, nie mogłem odmówić takiemu wyzwaniu.

nie chodzi żeby się całkiem ich pozbyć - tylko pozbyć się brzydkich warunków z logiki biznesowej aplikacji
można poczytać wpisując w google if-less programming: http://antiifcampaign.com/ http://alisnic.github.io/posts/ifless/

Przeczytełem z zainteresowaniem Twoje linki i przyznaje im 100% rację - wszelkie formy ifów są tylko niepotrzebnym komplikowaniem kodu. Postanowiłem pójść dalej - czemu eliminować tylko ify? Wyeliminujmy od razu wszelkie niebezpośrednie wywołania i abstrakcje. Postanowiłem napisać kod w najprostszy i najczytelniejszy możliwy sposób, bez żadnych utrudnień jak ify, pętle warunkowe, etc.

Moje eleganckie i wydajne rozwiązanie (bez żadnych hacków):

def beta(alpha):
    return (((((((((((((((((delta(alpha,0,0)*f+s)*f+delta(alpha,1,0))*f+s)*f+delta(alpha,
        2,0))*f+n)*f+delta(alpha,0,1))*f+s)*f+delta(alpha,1,1))*f+s)*f+delta(alpha,2,1))*
        f+n)*f+delta(alpha,0,2))*f+s)*f+delta(alpha,1,2))*f+s)*f+delta(alpha,2,2))*f+n)+0
def gamma(a,b,c): return (1-iota(a-b))*(1-iota(b-c))
def delta(alpha,x,y): return theta(epsilon(alpha,x,y))
def epsilon(alpha,x,y): return (alpha/(10**(x*3+y)))%10
def zeta(alpha, x, y, v): return (alpha-epsilon(alpha,x,y)*(10**(x*3+y)))+v*(10**(x*3+y))
def theta(x): return -12*x*x+45*x+46
def iota(x): return ((x>0)-(x<0))*((x>0)-(x<0))
def xi(x, a, b): return x*a+(1-x)*b

f = 256
s = 32
n = 10
alpha=0
nxt=1
rho=beta(alpha)

while True:
    print format(rho,'x').decode('hex')
    rho=0
    x,y=input()
    alpha_val=iota(epsilon(alpha,x,y))
    rho=xi(alpha_val,rho*18446744073709551616+7597139782223356938,rho)
    nxt=xi(alpha_val,nxt,(nxt+1)%2)
    alpha=xi(alpha_val,alpha,zeta(alpha,x,y,nxt+1))
    sigma=beta(alpha)
    rho=rho*22300745198530623141535718272648361505980416+sigma
    omega=iota(xi(gamma(epsilon(alpha,0,2),epsilon(alpha,1,1),epsilon(alpha,2,0)),epsilon(
        alpha,1,1),xi(gamma(epsilon(alpha,0,0),epsilon(alpha,1,1),epsilon(alpha,2,2)),
        epsilon(alpha,1,1),xi(gamma(epsilon(alpha,2,0),epsilon(alpha,2,1),epsilon(alpha,2,2)
        ),epsilon(alpha,2,0),xi(gamma(epsilon(alpha,1,0),epsilon(alpha,1,1),epsilon(alpha,1,
        2)),epsilon(alpha,1,0),xi(gamma(epsilon(alpha,0,0),epsilon(alpha,0,1),epsilon(alpha,
        0,2)),epsilon(alpha,0,0),xi(gamma(epsilon(alpha,0,2),epsilon(alpha,1,2),epsilon(
        alpha,2,2)),epsilon(alpha,0,2),xi(gamma(epsilon(alpha,0,1),epsilon(alpha,1,1),epsilon
        (alpha,2,1)),epsilon(alpha,0,1),xi(gamma(epsilon(alpha,0,0),epsilon(alpha,1,0),
        epsilon(alpha,2,0)),epsilon(alpha,0,0),0)))))))))
    rho=xi(omega,rho*79228162514264337593543950336+(563906476998359738053408*f*f+theta(omega)*f+n),rho)
    alpha=xi(omega,0,alpha)
    nxt=xi(omega,1,nxt)
    sigma=xi(omega,beta(alpha),sigma)
    rho=xi(omega,rho*22300745198530623141535718272648361505980416+sigma,rho)

Wszystko oczywiście bezproblemowo działa:

C:\Users\msm\Documents>python ox.py
. . .
. . .
. . .

1, 1
. . .
. O .
. . .

2, 2
. . .
. O .
. . X

1, 2
. . .
. O .
. O X

2, 1
. . .
. O X
. O X

1, 0
. O .
. O X
. O X
winner is O
. . .
. . .
. . .

2, 2
. . .
. . .
. . O

1, 0
. X .
. . .
. . O
0
msm napisał(a):

...

wow, jestem pod dużym wrażeniem :) pisałeś to w takiej formie czy obfuskowałeś na koniec?
Muszę przyznać że spodziewałem się że pojawi się choć jedno podobne rozwiązanie i obstawiałem na pythona, ale myślałem że ogółem zaproponowane rozwiązania będą wyglądać... trochę inaczej ;)

jako że nikt chyba już nie podejmie wyzwania to załączam swoje rozwiązanie które utworzyłem na próbę przed założeniem tematu (trochę oszukiwałem bo posłużyłem się wpfem do interfejsu)

Cieszę się że wyszło z tego tematu parę fajnych rzeczy i mogłem się trochę nauczyć (choćby tego że mov is Turing-complete :) )
Pozdrawiam, miłego weekendu

Jak ktoś jeszcze ma ciekawe rozwiązanie to może wstawić, nie mam mocy zamykać tematu :)

0

No dobra. To ja daję kolejne proste zadanie. Napisz program, który wczyta liczbę i wypisze tyle jedynek ile wynosi liczba. Warunki jak w poście początkowym.

Druga sprawa:

mov is Turing-complete

Poszukałem w guglu, G podał mi https://www.cl.cam.ac.uk/~sd601/papers/mov.pdf a tam jest takie stwierdzenie:

We are therefore
in a suitable state to run the program again, so we use our single
unconditional jump to loop back to the start:
jmp start
This simultes an arbitrary Turing machine, using only mov (and
a single jmp to loop the program).

No i niestety, ale trzeba jakiś skok wstawić.

  1. W jaki sposób kulawe emulowanie ifa za pomocą wzorca odwiedzający ma być lepsze od samego ifa?
0
Wibowit napisał(a):

Napisz program, który wczyta liczbę i wypisze tyle jedynek ile wynosi liczba.

Nikt nie zabraniał pętli jako takich, chodziło tylko o to żeby nie wykorzystać pętli jako zamiennika dla if:

if (a == b)
{
   // sth
}
while(a == b)
{
  // sth
  break;
}

Ale w większości języków znajdzie się odpowiednik dla:

for i in range(1, 10):
     print(i)

No i niestety, ale trzeba jakiś skok wstawić.

No tak mamy zbudowane komputery że wymagają skoków, jednak teoretycznie komputer mógłby być tak zaprojektowany żeby czytać programy w kółko i nie mieć instrukcji skoku ;) Ale temat nie jest o unikaniu skoków, więc rozumiem że nawiązujesz do tego co napisałem o pracy mózgu? Mózg raczej nie czyta rozkazów sekwencyjnie z innego obszaru mózgu, w zależności od wykonywanego zadania władzę przejmują różne obszary mózgu - nie jest to w żaden sposób podobne do tego jak działa komputer

W jaki sposób kulawe emulowanie ifa za pomocą wzorca odwiedzający ma być lepsze od samego ifa?

Teraz mnie trochę zbiłeś z tropu :| Kto i gdzie napisał że jest lepsze? Są programy gdzie jest to lepsze, są programy gdzie nie.
W If-less programming chodzi o to żeby unikać warunków na rzecz abstrakcji i wzorców, tam gdzie jest to stosowne oczywiście - nikt nie mówi o całkowitym zakazie używania warunków. Ten temat to zabawa w całkowity zakaz używania warunków. Myślałem że ująłem to jasno w pierwszym poście

4

Sorry, ale brzmisz jak narwaniec oderwany od rzeczywistości.

  1. Każdy system kompletny w sensie Turinga jest w stanie emulować dowolny inny komputer. Stąd, jeśli ktoś stworzy jakiś bezifowy system kompletny w sensie Turinga, to będzie on na przykład w stanie emulować programy skompilowane w C pod Windowsa. Inaczej mówiąc - wszystkie systemy zupełne w sensie Turinga są równe pod względem możliwości obliczeniowych. Różnica jest taka, że w jednych programowanie jest wygodne, a w innych jest równie łatwe co zakładanie majtek przez głowę.

  2. To, że robisz coś z małą ilością ifów nie znaczy, że to będzie wydajniejsze. Wąskim gardłem jest praktycznie zawsze przesyłanie danych. Stąd procesory mają wielopoziomową pamięć podręczną - by zmniejszyć czas dostępu do najczęściej wykorzystywanych danych. Jeżeli wyeliminujesz ify kosztem losowego (z perspektywy procesora) latania po pamięci to pogrążysz się wydajnościowo. Lokalne ify i pętle mają tę zaletę, że są właśnie lokalne i łatwo je utrzymać w pamięci podręcznej. Polimorfizm wiąże się ze skokami do funkcji zdefiniowanych w innych klasach, a te mogą już wypaść z cache, co spowoduje, że skok do funkcji będzie kosztowny. Ponadto, użycie funkcji wirtualnych (wszystkie niestatyczne funkcje w Javie są wirtualne, w C++ i C# trzeba je oznaczyć słówkiem virtual) wymusza chodzenie po v-table, które samo w sobie jest kosztowne i też wymaga odwołania do obszaru pamięci, który potencjalnie nie znajduje się w pamięci podręcznej.

  3. Kolega @Patryk27 już ci napisał, że w mózgu istnieją odpowiedniki ifów, a ty dalej brniesz w swoje wyobrażenia. W mózgu w każdej sekundzie wykonują się miliardy ifów. Pamiętam trochę jak przez mgłę, ale uczyłem się o tym jak działają synapsy w mózgu i jest tam warunkowe przekazywanie impulsów dalej. Za Wikipedią https://pl.wikipedia.org/wiki/Synapsa :

Mediator wypełnia szczelinę synaptyczną i część z jego cząsteczek łączy się z receptorami na błonie postsynaptycznej. Powoduje to otworzenie się kanałów dla jonów sodu, a w efekcie depolaryzację błony postsynaptycznej. Jeżeli depolaryzacja ta osiągnie wartość progową, otwierają się kolejne kanały sodowe wrażliwe na napięcie, skutkiem czego pojawia się potencjał czynnościowy i indukuje falę przechodzącą przez cały neuron.

Czyli synapsa działa w ogromnym uproszczeniu tak:
if (siła_na_wejściu > 5) jedziemy_dalej();
Synaps jest znacznie więcej niż neuronów. Za https://pl.wikipedia.org/wiki/Neuron :

Szacuje się, że ludzki mózg zawiera ok. 1,5-1,6 x 1e11 neuronów[2][3] i 1e14 synaps[4].

Stąd ilość pętli z rzędem ifów w mózgu jest gigantyczna.

  1. Kolega @winerfresh podał ci przykład z konstrukcją match. W różnych językach pattern matching ma różną elastyczność. Ja programuję w Scali i tam też jest pattern matching i to z całkiem dużymi możliwościami. Na poziomie bajtkodu pattern matching rozwijany jest do drabinki ifów, gdyż ekstraktorami mogą być dowolne funkcje i nie da się ich statycznie przeanalizować, by stworzyć jakąś tablicę skoków.

Znalazłem jakiś artykuł o chwytliwej nazwie: Towards IF-less Programming
Gość podnieca się pattern matchingiem, stwierdza, że obniża on ilość ifów. Ale co z tego, że obniża skoro sam redukuje się do drabinki ifów na etapie kompilacji?

  1. Jeżeli chodzi ci tylko o pisanie czystego kodu, to jest chociażby książka "Czysty kod" Boba Martina i tam jest wiele rzeczy objaśnione. Jest opisanych wiele więcej wzorców niż tylko Odwiedzający. Nie rozumiem po co robić mnóstwa szumu z powodu jednego wzorca.

Podsumowując, jest za dużo szumu (naiwnego podniecania się) do sygnału (konkretnej informacji, nt tego co jest złe, co jest dobre i dlaczego).

0

Zabawa nie polegała na tym że coś ma być wydajniejsze, pozdrawiam

0

Spoko, ale ja nie pisałem o zabawie. Zacząłeś temat od zabawy, ale potem od razu przeszedłeś do ewangelizowania nas o tym jakie to ify są wstrętne. Chciałbym, żebyś napisał coś konkretnego o programowaniu bezifowym.

W zespole mam kolegę, który nie lubi ifów, które mają else i nie lubi też dziedziczenia, skłaniając się ku kompozycji i dopasowywaniu wzorców. Nie przedstawił żadnego przekonującego wyjaśnienia i argumentacji, stąd jego wywody nie odniosły skutku. Podobny przypadek jest w tym wątku.

1

Przerób trochę kod z stupid template tricks korzystając z tej liby do patten matchingu i masz co chciałeś.

0
Wibowit napisał(a):

No dobra. To ja daję kolejne proste zadanie. Napisz program, który wczyta liczbę i wypisze tyle jedynek ile wynosi liczba. Warunki jak w poście początkowym.

Może być js? ;)

function Counter() {
	this.result = '';
  
	this[0] = function(){
		document.write(this.result);
	};
	
	this[1] = function(n){
		this.result += '1'; 
		this.next(n - 1);
	};
	
	this.next = function(n) {
		this[Math.ceil(n / (n + 1))](n);
	};
}

var n = prompt('How many?');
new Counter().next(n);

https://jsfiddle.net/gmauLxgb/1/

To akurat proste. Z tym, że jakoś nie widzę sensu pisania kodu na siłę w ten sposób :P

0
Wibowit napisał(a):

Zacząłeś temat od zabawy, ale potem od razu przeszedłeś do ewangelizowania nas o tym jakie to ify są wstrętne.

hę? Napisałem tylko że jest taka religia i stąd pomysł na zadanie. I że prawdopodobnie jest możliwe napisanie dowolnego programu bez warunków jako ciekawostka
Osobiście nie lubię tylko gdy kod jest naszprycowany ifologią i zazwyczaj łamaniem przy tym LSP, ale całkowity brak ifów tylko by pogorszył sprawę

9

wow, jestem pod dużym wrażeniem :) pisałeś to w takiej formie czy obfuskowałeś na koniec?
Muszę przyznać że spodziewałem się że pojawi się choć jedno podobne rozwiązanie i obstawiałem na pythona, ale myślałem że ogółem zaproponowane rozwiązania będą wyglądać... trochę inaczej ;)

Chętnie podałbym cały tok rozwiązywania/zmieniania zadania, ale niestety - został w pracy (No co, piątek. Niektórzy przeglądają kwejka, ja pół godziny pisałem kółko i krzyżyk). W sumie zrobiłem też czysto funkcyjne rozwiązanie dla @shaloma, ale zapomniałem wrzucić.

wow, jestem pod dużym wrażeniem :) pisałeś to w takiej formie czy obfuskowałeś na koniec?
Muszę przyznać że spodziewałem się że pojawi się choć jedno podobne rozwiązanie i obstawiałem na pythona, ale myślałem że ogółem zaproponowane rozwiązania będą wyglądać... trochę inaczej ;)

Jeszcze bonusowa wersja dla @shaloma - same funkcje, żadnych mutowalnych zmiennych:

def beta(alpha):
    return (((((((((((((((((delta(alpha,0,0)*256+32)*256+delta(alpha,1,0))*256+32)*256+delta(alpha,
        2,0))*256+10)*256+delta(alpha,0,1))*256+32)*256+delta(alpha,1,1))*256+32)*256+delta(alpha,2,1))*
        256+10)*256+delta(alpha,0,2))*256+32)*256+delta(alpha,1,2))*256+32)*256+delta(alpha,2,2))*256+10)+0
def gamma(a,b,c): return (1-iota(a-b))*(1-iota(b-c))
def delta(alpha,x,y): return theta(epsilon(alpha,x,y))
def epsilon(alpha,x,y): return (alpha/(10**(x*3+y)))%10
def zeta(alpha, x, y, v): return (alpha-epsilon(alpha,x,y)*(10**(x*3+y)))+v*(10**(x*3+y))
def theta(x): return -12*x*x+45*x+46
def iota(x): return ((x>0)-(x<0))*((x>0)-(x<0))
def xi(x, a, b): return x*a+(1-x)*b
 
def xx(omega,alpha,nxt,rho):
    ox(xi(omega,0,alpha), xi(omega,1,nxt), xi(omega,xi(omega,rho*79228162514264337593543950336+
        (563906476998359738053408*256*256+theta(omega)*256+10),rho)*22300745198530623141535718272648361505980416+
        xi(omega,beta(xi(omega,0,alpha)),beta(xi(omega,0,alpha))),xi(omega,rho*79228162514264337593543950336+
        (563906476998359738053408*256*256+theta(omega)*256+10),rho)))

def xo(x, alpha, nxt, rho):
    xx(iota(xi(gamma(epsilon(alpha,0,2),epsilon(alpha,1,1),epsilon(alpha,2,0)),epsilon(
        alpha,1,1),xi(gamma(epsilon(alpha,0,0),epsilon(alpha,1,1),epsilon(alpha,2,2)),
        epsilon(alpha,1,1),xi(gamma(epsilon(alpha,2,0),epsilon(alpha,2,1),epsilon(alpha,2,2)
        ),epsilon(alpha,2,0),xi(gamma(epsilon(alpha,1,0),epsilon(alpha,1,1),epsilon(alpha,1,
        2)),epsilon(alpha,1,0),xi(gamma(epsilon(alpha,0,0),epsilon(alpha,0,1),epsilon(alpha,
        0,2)),epsilon(alpha,0,0),xi(gamma(epsilon(alpha,0,2),epsilon(alpha,1,2),epsilon(
        alpha,2,2)),epsilon(alpha,0,2),xi(gamma(epsilon(alpha,0,1),epsilon(alpha,1,1),epsilon
        (alpha,2,1)),epsilon(alpha,0,1),xi(gamma(epsilon(alpha,0,0),epsilon(alpha,1,0),
        epsilon(alpha,2,0)),epsilon(alpha,0,0),0))))))))),alpha,nxt,rho)

def ox(alpha, nxt, rho):
    print format(rho,'x').decode('hex')
    x=input()
    xo(x, xi(iota(epsilon(alpha,x[0],x[1])),alpha,zeta(alpha,x[0],x[1],xi(iota(epsilon(alpha,
        x[0],x[1])),nxt,(nxt+1)%2)+1)), xi(iota(epsilon(alpha,x[0],x[1])),nxt,(nxt+1)%2),
        xi(iota(epsilon(alpha,x[0],x[1])),7597139782223356938,0)*22300745198530623141535718272648361505980416
        +beta(xi(iota(epsilon(alpha,x[0],x[1])),alpha,zeta(alpha,x[0],x[1],xi(iota(epsilon(alpha,x[0],x[1]))
        ,nxt,(nxt+1)%2)+1))))

ox(0, 1, beta(0))

A odnośnie tego jak to pisałem - miałem gdzieś zapisywane krok po kroku transformacje (właśnie jakby trzeba było się pochwalić "jak to było zrobione", ale niestety nie na tym komputerze, więc postaram się odtworzyć.

Ogólnie cały kod "oblicza" wyjście jako liczbę, a następnie wypisuje ją jako napis.

  1. Zacząłem od napisania klasycznego kółka i krzyżyka.
    1.5) Zmieniłem reprezentacje planszy tak, żeby '0' oznaczało puste pole, '1' oznaczało kółko, a '2' oznaczało krzyżyk - taka liczbowa reprezentacja okazała się najlepsza.
  2. Przepisałem wszystkie printy w ten sposób, żeby został tylko jeden print na końcu. Czyli zamiast:
print 'a'
...
print 'b'

zrobiło się

to_print = 'a'
...
to_print = to_print + 'b'
  1. pierwsza trudniejsza część - przepisałem wszystkie ify itp na wyrażenia warunkowe. Czyli zamiast:
winner = get_winner(board)
if winner != 0:
    to_print = to_print + 'winner is ' + get_symbol(winner)
    board = [[0,0,0],[0,0,0],[0,0,0]
    next_player = 1

robi się:

winner = get_winner(board)
to_print = to_print + 'winner is ' + get_symbol(winner) if winner != 0 else to_print
board = [[0,0,0],[0,0,0],[0,0,0] if winner != 0 else board
next_player = 1 if winner != 0 else next_player

etc

Jest problem, bo używamy napisów oraz list. Niezdyt dobrze się one komponują z operacjami matematycznymi, a takich zamierzamy używać (for fun and profit). Więc zaczynamy od zmiany napisów na liczby - np. zamiast

print' 'winner_is '

byłoby

print format(563906476998359738053408, 'x').decode('hex')

Ale w praktyce nie wypisywałem nigdzie stringów a je łączyłem (jak w punkcie 2 napisane), więc trzeba było wymyśleć sposób na połączenie dwóch "liczbowych" napisów. To tak naprawdę dość prostę - jeśli mamy liczby reprezentujące napisy "A" i "B", to żeby uzyskać napis "AB" musimy obliczyć A * 256^(długość_napisu_b) + B.

Pozamieniałem w ten sposób wszystkie literały napisowe (wszystkie napisy mają stałą długość na szczęście) w kodzie.

  1. Drugi problem, plansza jest listą. Ale jako że ta lista może przechowywac tylko liczby 0-2, możemy przechowywać cały stan planszy w jednej wielkiej liczbie. Np. weźmy planszę (1-kółko, 2-krzyżyk):
102
201
101

Możemy ją potraktować jako liczbę dziesiętną (albo trójkową, albo szesnastkową, albo dziewiątkową) równą 102201101. Wtedy zamiast

x = board[0][1]
board[1][2] = 2

Trzeba pisać:

x = get(board, 0, 1)
board = set(board, 1, 2, 2)

Gdzie:

# epsilon = get
def epsilon(alpha,x,y): return (alpha/(10**(x*3+y)))%10
# zeta = set
def zeta(alpha, x, y, v): return (alpha-epsilon(alpha,x,y)*(10**(x*3+y)))+v*(10**(x*3+y))

Ale warto, bo teraz w kodzie operujemy na stamych liczbach.

  1. Skoro już mamy same liczby, to wypada zacząć redukować ify (bo na razie je tylko przesywaliśmy z miejsca w miejsce).

Po pierwsze - mały "matematyczny" hack na ifa (ale to nie jest żaden if oczywiście, tylko prosta funkcja liniowa):

# xi == if
def xi(x, a, b): return x*a+(1-x)*b

# xi(0, a, b) === b
# xi(1, a, b) === a

Niestety nie działa to tak jak chcemy jeśli x jest liczbą spoza zbioru {0, 1} - ale można zrobić funkcję "to_bool" która zamienia dowolną liczbę nierówną 0 na 1, a 0 zostawia w spokoju.

Jedną z prostych definicji takiej funkcji jest:

def to_bool(x):
    return sgn(x)*sgn(x) # sgn(x)**2, wartość bezwzględna znaku liczby

# gdzie sgn to
def sgn(x):
    return (x>0)-(x<0)

W podanym kodzie:

def iota(x): return ((x>0)-(x<0))*((x>0)-(x<0))

Przy okazji, negacja boola 'x' to oczywiście 1-x.
A sprawdzenie równości liczb x i y, zamiast używać operatora ==, można zrealizować jako 1-to_bool(x - y). (Wiem, w pythonie wystarczy x == y, bo boole można traktować jak liczby. Powiedzmy że miałem zaćmienie i niepotrzebnie skomplikowałem kod.)

  1. skoro mamy już takiego pseudo-ifa, czas go wykorzystać. Weźmy poprzedni kod:
winner = get_winner(board)
to_print = XYZ if winner != 0 else to_print
board = UVW if winner != 0 else board
next_player = 1 if winner != 0 else next_player

Możemy przepisać to na:

winner = to_bool(get_winner(board))
to_print = xi(winner, XYZ, to_print)
board = xi(winner, UVW, board)
next_player = xi(winner, 1, next_player)

No i to w sumie wszystko, jedne rzeczy bylo prościej podmienić inne trudniej. Z ciekawostek, była taka funkcja:

def getsymbol(x):
    if x == 0:
        return ord('.') # ord, bo wszędzie w ostatecznej wersji korzystamy z liczb
    elif x == 1:
        return ord('O')
    elif x == 2:
        return ord('X')

I trzeba było usunąć z niej ify. Najpierw, co dokładnie zwracamy:

>>> ord('.')
46
>>> ord('O')
79
>>> ord('X')
88

Więc chcemy funkcję spełniającą własność:

f(0) = 46
f(1) = 79
f(2) = 88

Najprościej wykorzystać wielomian axx + b*x + c:

a*0*0 + b*0 + c = 46
a*1*1 + b*1 + c = 79
a*2*2 + b*2 + c = 88

Kilkanaście lat edukacji nie poszło na marne, i szybko znajdujemy współczynniki:

def theta(x):
    return -12*x*x+45*x+46

(@niezdecydowany - i widzisz jaka matematyka jest przydatna w życiu)

edit:

Druga sprawa:

mov is Turing-complete

Poszukałem w guglu, G podał mi https://www.cl.cam.ac.uk/~sd601/papers/mov.pdf a tam jest takie stwierdzenie:

To prawda, podzbiór pythona który wykorzystałem (brak żadnych operacji warunkowych) nie jest kompletny w sensie maszyny turinga.
Na pewno ma co najmniej moc maszyny turinga liniowo ograniczonej, ale być może jest troche silniejszy (musiałbym dokładniej przeanalizować).

edit2: post pisany podczas jazdy trzęsącym się busem, więc wszelkie literówki/błedy niezamierzone.

0
Wibowit napisał(a):

No dobra. To ja daję kolejne proste zadanie. Napisz program, który wczyta liczbę i wypisze tyle jedynek ile wynosi liczba. Warunki jak w poście początkowym.

No nie popisałeś się ;D

if __name__ == '__main__':
    value = input()
    print("1" * int(value))
0
Wibowit napisał(a):

No dobra. To ja daję kolejne proste zadanie. Napisz program, który wczyta liczbę i wypisze tyle jedynek ile wynosi liczba. Warunki jak w poście początkowym.

[1..System.Convert.ToInt32(System.Console.ReadLine())] |> List.map (fun x -> "1") |> List.reduce(fun x y -> x + y) |> printfn "%s"

Alternatywnie:

[1..System.Convert.ToInt32(System.Console.ReadLine())] |> List.map (fun x -> printf "1")

Gwarantuję, że w moim kodzie nie ma żadnej instrukcji porównania, ale w map/reduce pewnie już są :P

1 użytkowników online, w tym zalogowanych: 0, gości: 1