Nikt póki co nie wyraził zainteresowania rozwiązywaniem pracy domowej, toteż skorzystam ze starych rozwiązań, jedynie przepiszę je w Rust.
Pierwsze rozwiązanie przyszło od Kury Czubatej, nazajutrz od publikacji lekcji.
Wersja w Golang:
package main
func main() {
println("888 d8P 888 888 8888888b. d8888 888 888")
println("888 d8P 888 888 888 Y88b d88888 888 888")
println("888 d8P 888 888 888 888 d88P888 888 888")
println("888d88K 888 888 888 d88P d88P 888 .d8888b 88888888 888 888 88888b. 8888b. 888888 8888b.")
println("8888888b 888 888 8888888P\" d88P 888 d88P\" d88P 888 888 888 \"88b \"88b 888 \"88b")
println("888 Y88b 888 888 888 T88b d88P 888 888 d88P 888 888 888 888 .d888888 888 .d888888")
println("888 Y88b Y88b. .d88P 888 T88b d8888888888 Y88b. d88P Y88b 888 888 d88P 888 888 Y88b. 888 888")
println("888 Y88b \"Y88888P\" 888 T88b d88P 888 \"Y8888P 88888888 \"Y88888 88888P\" \"Y888888 \"Y888 \"Y888888\"")
}
Wersja w Rust:
fn main() {
println!("888 d8P 888 888 8888888b. d8888 888 888");
println!("888 d8P 888 888 888 Y88b d88888 888 888");
println!("888 d8P 888 888 888 888 d88P888 888 888");
println!("888d88K 888 888 888 d88P d88P 888 .d8888b 88888888 888 888 88888b. 8888b. 888888 8888b.");
println!("8888888b 888 888 8888888P\" d88P 888 d88P\" d88P 888 888 888 \"88b \"88b 888 \"88b");
println!("888 Y88b 888 888 888 T88b d88P 888 888 d88P 888 888 888 888 .d888888 888 .d888888");
println!("888 Y88b Y88b. .d88P 888 T88b d8888888888 Y88b. d88P Y88b 888 888 d88P 888 888 Y88b. 888 888");
println!("888 Y88b \"Y88888P\" 888 T88b d88P 888 \"Y8888P 88888888 \"Y88888 88888P\" \"Y888888 \"Y888 \"Y888888\"");
}
I wynik:
Ładny efekt z generatora ASCIIart. Problemem było zderzenie ze znakami, które nie były rozumiane jako część ciągu znaków.
Chodzi o to, że ciąg znaków (string) jest zapisywany jako coś pomiędzy dwoma cudzysłowami. W jaki sposób zatem wstawić do niego cudzysłów? Rozwiązaniem jest zastosowanie tzw. znaku modyfikacji (lub znaku ucieczki, lub escape’owania; ten ostatni zwrot jest najpopularniejszy). Języki programowania mają takie znaki zdefiniowane. W przypadku Go jest to backslash (ukośnik odwrócony): . Jak widać, przed każdym znakiem cudzysłowu w tekście umieszczony jest backslash. Jeśli teraz zastanawiacie się, jak umieścić w tekście backshlash, to podpowiem, że wystarczy go wyescape’ować (zapisać dwa backslashe jeden po drugim).
Znak modyfikacji ma też inne zastosowania – umożliwia wprowadzenie do kodu znaków niewidocznych dla oka:
- Znak nowej linii: \n,
- Znak powrotu karetki: \r,
- Tabulator: \t,
i kilka innych.
Jeśli ktoś kiedyś pisał na maszynie do pisania, kojarzy zapewne, że aby zacząć nową linię, trzeba było obrócić rolkę, na której znajdował się papier, i pociągnąć wajchę, aby przesunąć w prawo papier, a literki były znów pisane na początku kartki. Element, który się przesuwa to właśnie karetka, a jego przesunięcie to powrót. Stąd też w tabeli znaków występują znaki nowej linii i powrotu karetki. Oczywiście ich wykorzystanie zostało ustandaryzowane, a jak wiadomo, dobre w standardach jest to, że jest ich tak mnóstwo do wyboru. Zatem, znak nowej linii to:
- \r\n m.in. w Windowsach,
- \r m.in. w starych Macach,
- \n w Uniksach, Linuksach i nowych Macach.
Znając życie, prędzej czy później będziemy musieli sobie z tym poradzić w którymś programie, ale na razie tyle wystarczy.
Wrażenia z przepisania z Golang do Rust są takie: musiałem dodać wykrzyknik z println
na println!
i zakończyć każdą linię średnikiem.
Jedne języki mają średniki, inne nie mają, jeszcze inne mają, ale nie wymagają poza kilkoma przypadkami. Średnik jest używany aby zaznaczyć, że oto właśnie kończy się pojedyncze wyrażenie.
Wykrzyknik ma znaczenie, które rozumiem, ale nie ogarniam jeszcze jego intencji. Może najpierw wyjaśnię. Programowanie w dużej mierze polega na wywoływaniu różnych funkcji. Funkcje pochodzą z bibliotek - takich niby programów, które same w sobie zazwyczaj nie dostarczają czegoś, co można uruchomić inaczej niż jako cząstkę własnego programu (który również może być biblioteką). Języki programowania oferują zazwyczaj tak zwane biblioteki standardowe. Z takiej biblioteki pochodzi w jezyku Go println. W Rust nie ma funkcji println, jest za to makro. Makro ma to wspólnego z funkcją, że dostarcza jakąś operację, którą można wykonać wielokrotnie bez powtarzania jej opisu. Makro różni się od funkcji wykrzyknikiem po nazwie (w Rust), a ogólnie tym, że funkcja siedzi sobie gdzieś w pamięci i program musi pamiętać, że musi tam pójść, aby ją wykonać, a makro jest jakby szablonem i jego wywołanie jest przy okazji kompilacji zastępowane całkiem tą definicją i potem nie funkcjonuje jako osobne coś, do czego program musi przyjść. W chwili obecnej nie ma to dla nas większego znaczenia - wołamy coś i na ekranie widać wynik. Być może kiedyś zrozumiemy, co podmiot liryczny miał na celu.
Drugi program przysłała Melawen.
Go:
package main
func main() {
println(" Nudno jest tu bez ciebie. Nudno do obłędu!\nJestem jeszcze wraz z wiewiórką i pieskiem,\nPiszę, czytam i palę, wciąż mam oczy niebieskie,\nLecz to wszystko tylko siłą rozpędu.\n")
println("Wciąż jeszcze świt jest szary, zmierzch niebiesko -złoty,\nDzień przechodzi na jedną, noc na drugą stronę\nI róże zakwitają bez wielkiej ochoty\nBo tak są już przyzwyczajone.\n")
println("A jednak świat się skończył. Czy wy rozumiecie?\nŚwiata nie ma i ja go nie stworzę.\nCzas jest równy i cichy. Lecz czekajcie... może -\nMoże ja jestem już na tamtym świecie?\n")
}
Rust:
fn main() {
println!(" Nudno jest tu bez ciebie. Nudno do obłędu!\nJestem jeszcze wraz z wiewiórką i pieskiem,\nPiszę, czytam i palę, wciąż mam oczy niebieskie,\nLecz to wszystko tylko siłą rozpędu.\n");
println!("Wciąż jeszcze świt jest szary, zmierzch niebiesko -złoty,\nDzień przechodzi na jedną, noc na drugą stronę\nI róże zakwitają bez wielkiej ochoty\nBo tak są już przyzwyczajone.\n");
println!("A jednak świat się skończył. Czy wy rozumiecie?\nŚwiata nie ma i ja go nie stworzę.\nCzas jest równy i cichy. Lecz czekajcie... może -\nMoże ja jestem już na tamtym świecie?\n");
}
I wynik:
Dwie sprawy, na które warto zwrócić uwagę:
spacja na początku pierwszego stringa (ciągu znaków, tak się zazwyczaj go nazywa w programowaniu; będzie o tym kiedy indziej) wyświetla się w wyniku – wynika to z tego, że białe znaki nie są ignorowane,
mimo że na końcu każdej strofy jest jeden znak nowej linii, wyświetlają się dwa – Melawen użyła funkcji
println
(makraprintln!
), która umieszcza na końcu drukowanego tekstu znak nowej linii.
Melawen zadała też pytanie o możliwość wygodniejszego zapisu tekstu. Niektóre języki programowania pozawalają bowiem rozbić string na wiele linii. Rust pozwala to zapisać na trzy sposoby:Zwyczajnie walisz nowe linie między cudzysłowami. Rust rozumie, że ma wtedy wczytać wszystkie znaki: i nowe linie i spacje, stąd kolejne wiersze muszą być pisane bez spacji na początku:
println!("Wiersz1
Wiersz2
Wiersz3");
- Wyraźnie piszesz, że chcesz znak nowej linii, a następnie dajesz backslash
\
tuż przed nową linią, co oznacza w skrócie "zignoruj znak nowej linii i wszystkie spacje na początku następnej linijki":
println!("Wiersz1\n\
Wiersz2\n\
Wiersz3");
- Wyraźnie piszesz, że ten string to nie jest taki zwykły string tylko tak zwany surowy, który nie ma tylko cudzysłowów, ale zaczyna się od
r#"
a kończy"#
. W praktyce wygląda trochę jak ten pierwszy sposób, ale zdaje się, że tu nie ma żadnych znaków modyfikacji ani niczego - wszystko jest przedstawione w pamięci dokładnie tak, jak się napisało:
println!(
r#"Wiersz1
Wiersz2
Wiersz3"#);
Mały konkurs: jakiego ciągu znaków nie da się zapisać w surowym stringu?
Rudominka przysłała swoje rozwiązanie.
Go:
package main
func main() {println("Halina Poświatowska")
println("Jeśli nie przyjdziesz...")
println("świat będzie uboższy")
println("o tę trochę miłości")
println("o pocałunki które nie sfruną")
println("w otwarte okno")
println("świat będzie chłodniejszy")
println("o tę czerwień")
println("która nagłym przypływem")
println("nie rozżarzy moich policzków")
println("świat będzie cichszy")
println("o ten gwałtowny stukot")
println("serca poderwanego do lotu")
println("o skrzyp drzwi")
println("otwieranych na oścież")
println("drgający żywy świat")
println("zastygnie")
println("w kształt doskonały nieomal")
println("geometryczny")
}
Rust:
fn main() {println!("Halina Poświatowska");
println!("Jeśli nie przyjdziesz...");
println!("świat będzie uboższy");
println!("o tę trochę miłości");
println!("o pocałunki które nie sfruną");
println!("w otwarte okno");
println!("świat będzie chłodniejszy");
println!("o tę czerwień");
println!("która nagłym przypływem");
println!("nie rozżarzy moich policzków");
println!("świat będzie cichszy");
println!("o ten gwałtowny stukot");
println!("serca poderwanego do lotu");
println!("o skrzyp drzwi");
println!("otwieranych na oścież");
println!("drgający żywy świat");
println!("zastygnie");
println!("w kształt doskonały nieomal");
println!("geometryczny");
}
i wynik:
Na duży plus podanie autora wiersza. Pisząc kod wkraczamy w obszar praw autorskich, stąd warto pilnować tego – chyba nie chciałybyście być pominięte przy swoim dziele.
Jak widać w linii z deklaracją funkcji main
, stosowanie znaków nowej linii między poleceniami nie jest konieczne (przynajmniej dopóki kompilator rozumie kod). Jednak dobrą praktyką jest niepakowanie nadmiernej ilości kodu w jedną linijkę. Tutaj przykład mało wyrazisty niestety, lepiej "prezentuje" się przykładowo wiersz Melawen lub ASCIIart KuryCzubatej – trzeba się naprzewijać, aby dojść do końca linii.
W projektach informatycznych spora uwaga jest przywiązywana do formatowania kodu. Przyczyny są bardzo praktyczne – gdy stosuje się te same zasady formatowania, łatwiej czytać kod i łatwiej czyta się zmiany w nim.
Nie jest to nowy problem, stąd też narzędzia programistyczne zawierają zazwyczaj jakieś funkcje, które pomagają w tej kwestii. Nie inaczej jest w IntelliJ – aby sformatować kod, wystarczy wcisnąć Alt+Ctrl+L. Niektóre języki programowania (tak jak Go) mają własne narzucone formatowanie kodu, inne dają możliwość konfiguracji i dostosowywania do najdrobniejszych detali. To może być zgubna możliwość.
Ania jest miłośniczką jednej linii.
Go:
package main
func main() {
println(" _\n /\\ (_)\n / \\ _ __ _ __ _\n / /\\ \\ | '_ \\| |/ _` |\n / ____ \\| | | | | (_| |\n/_/ \\_\\_| |_|_|\\__,_|\n")
}
Rust:
fn main() {
println!(" _\n /\\ (_)\n / \\ _ __ _ __ _\n / /\\ \\ | '_ \\| |/ _` |\n / ____ \\| | | | | (_| |\n/_/ \\_\\_| |_|_|\\__,_|\n");
}
Po rozkodowaniu wychodzi ładne takie coś:
Ładna czcionka 🙂 Ania, jak widać, zastosowała znaki nowej linii i escape’owanie backslashy. Znak nowej linii na końcu zbędny przy wykorzystaniu funkcji println.
Na kwadrans przed północą (wykorzystując, że u mnie jest ona godzinę później) swoją pracę domową wrzuciła Asia.asia.
Go:
/**********************************/
/* HW-1.go */
/* Pierwsza praca domowa */
/* autor: Asia.asia */
/* 31/12/2015 */
/**********************************/
package main
func main() {
print(`Wlazł kotek
na płotek
i mruga
ładna to,
piosenka
niedługa`)
}
Rust:
/**********************************/
/* HW-1.go */
/* Pierwsza praca domowa */
/* autor: Asia.asia */
/* 31/12/2015 */
/**********************************/
fn main() {
print!("Wlazł kotek
na płotek
i mruga
ładna to,
piosenka
niedługa")
}
I wynik:
Podam jedynie że Asia.asia korzysta z kompendium wszelkiej wiedzy programistycznej, https://stackoverflow.com. Jeśli Twój problem nie został tam opisany, prawdopodobnie nie istnieje. W takiej sytuacji możliwe, że warto pomyśleć o odwiedzeniu siostrzanej strony https://psychology.stackexchange.com/
Ciekawy wydaje się nagłówek. W projektach często wykorzystuje się nagłówek. Amerykańskie firmy chyba nawet muszą go umieszczać, aby chronić prawnie zawartość pliku. Wiele osób go stosuje i zawiera w nim historię zmian, opis zawartości pliku, twórcę etc. Na przyszłość polecam jednak spojrzeć na datę ;) (W pierwotnej wersji wpis ten był publikowany w maju 2016). W mojej obecnej pracy nie stosuje się zbyt dużych nagłówków. Cała historia zmian w pliku znajduje się w systemie kontroli wersji (takim oprogramowaniu do zarządzania zmianami w kodzie, będzie o tym). Zaś co do opisu przeznaczenia – jeśli nazwa pliku i kod sam nie tłumaczy, do czego służy i co robi, to trzeba to naprawić. Jeden z moich kolegów stosował pewną strategię: dawał kod ze swojego projektu do przeczytania komuś z innego zespołu. Jeśli ten ktoś nie potrafił powiedzieć, co ten kod ma zrobić, trzeba było go poprawić.
Nagłówek został zapisany w komentarzach. Komentarz to taka część pliku z kodem, która zostaje w zupełności zignorowana przez kompilator. Aby zakomentować całą linię lub jej część, należy użyć sekwencji //
- wszystko po niej do nowej linii zostanie zakomentowane. Jest jeszcze komentarz blokowy - wszystko między /*
a */
zostanie zakomentowane. Z tego skorzystała Asia.
Żeby nie było, nagłówek ma też swój walor estetyczny, a ja nie jestem temu przeciwny, choć zazwyczaj nie czuję potrzeby, by go mieć.
Ale za ten przecinek to… Ranisz moje zaburzenia obsesyjno-kompulsyjne.
To by było na tyle. Pięć prac w terminie, chyba nie jest źle. Jeśli dostanę kolejne prace, będę aktualizował wpis, a przynajmniej starał się odpisywać autorkom. Jeśli chcecie się pochwalić swoimi programami, zachęcam do komentowania.