{ "metadata": { "language": "ruby", "name": "", "signature": "sha256:35553e771fe7ec3c59d3c39649329e55bb009ac133b42a86c79042d05422267a" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Wyj\u0105tki oraz operacje na plikach" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Wyj\u0105tki" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wyj\u0105tki s\u0105 mechanizmem wyst\u0119puj\u0105cym w wielu j\u0119zykach programowani. S\u0142u\u017c\u0105 one do obs\u0142ugi sytuacji nietypowych, w szczeg\u00f3lno\u015bci sytuacji b\u0142\u0119dnych. \u0179r\u00f3d\u0142o b\u0142\u0119du mo\u017ce by\u0107 r\u00f3\u017cne:\n", "* b\u0142\u0105d w kodzie programu, np. odwo\u0142anie do niezainicjowanej zmiennej,\n", "* wprowadzenie b\u0142\u0119dynych danych przez u\u017cytkownika, np. niepoprawnego adresu URL,\n", "* b\u0142\u0105d zwi\u0105zany ze \u015brodowiskiem pracy, np. zerwanie po\u0142\u0105czenia internetowego.\n", "\n", "Mechanizm wyj\u0105tk\u00f3w umo\u017cliwia obs\u0142ug\u0119 wszystkich typ\u00f3w b\u0142\u0119d\u00f3w w ujednolicony spos\u00f3b. Dodatkowo, w sytuacji, w kt\u00f3rej b\u0142\u0105d wyst\u0105pi, pozwala szybko zorientowa\u0107 si\u0119 jakie jest \u017ar\u00f3d\u0142o problemu (w przeciwie\u0144stwie do kompunikatu `Segmentation fault` znanego z j\u0119zyka C). \n", "\n", "Je\u015bli w wyniku wykonania programu spr\u00f3bujemy podzieli\u0107 przez zero, to wyst\u0105pi b\u0142\u0105d `ZeroDivisionError`:\n", "```ruby\n", "a = 1/0\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Opis b\u0142\u0119du sk\u0142ada si\u0119 z nazwy b\u0142\u0119du: `ZeroDivisionError`, komunikatu: `divided by 0` oraz tzw. *stacktrace'a*, czyli ci\u0105gu wywo\u0142a\u0144, kt\u00f3re doprowadzi\u0142y do b\u0142\u0119du. Poniewa\u017c Ruby nie jest kompilowany, stacktrace zawiera wskazanie plik\u00f3w i numer\u00f3w linii, w kt\u00f3rych wyst\u0119puje wywo\u0142anie, kt\u00f3re doprowadzi\u0142o do wyst\u0105pienia b\u0142\u0119du. \u015aledz\u0105c stacktrace, zwykle mo\u017cna do\u015b\u0107 szybko zidentyfikowa\u0107 miejsce, kt\u00f3re wymaga naprawy.\n", "\n", "Je\u015bli np. pr\u00f3bujemy rozwi\u0105za\u0107 niebanalny problem polegaj\u0105cy na okre\u015bleniu ile potrzeba trabant\u00f3w do przewiezienia grupy s\u0142oni (a liczba s\u0142oni mieszcz\u0105cych si\u0119 w trabancie podawana jest parametrem programu) mo\u017cemy napisa\u0107 nast\u0119puj\u0105cy kod:\n", "```ruby\n", "def liczba_miejsc_w_trabancie\n", " 4\n", "end\n", "\n", "def oblicz_liczbe_pelnych_samochodow(liczba_sloni)\n", " liczba_sloni / liczba_miejsc_w_trabancie\n", "end\n", "\n", "def oblicz_liczbe_niepelnych_samochodow(liczba_sloni)\n", " if liczba_sloni % liczba_miejsc_w_trabancie == 0\n", " 0\n", " else\n", " 1\n", " end\n", "end\n", "\n", "def oblicz_liczbe_wszystkich_samochodow(liczba_sloni)\n", " oblicz_liczbe_pelnych_samochodow(liczba_sloni) + oblicz_liczbe_niepelnych_samochodow(liczba_sloni)\n", "end\n", "\n", "def ile_potrzeba_samochodow?(liczba_sloni)\n", " puts \"Aby przewie\u017a\u0107 #{liczba_sloni} s\u0142oni potrzeba #{oblicz_liczbe_wszystkich_samochodow(liczba_sloni)} samochod\u00f3w.\"\n", "end\n", "\n", "ile_potrzeba_samochodow?(10)\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Je\u015bli teraz kto\u015b zmieni definicj\u0119 funkcji `liczba_miejsc_w_trabancie`:\n", "```ruby\n", "# wszystkie trabanty s\u0105 zajmowane przez myszy, dlatego nie mo\u017cna ju\u017c w nich umieszcza\u0107 s\u0142oni\n", "def liczba_miejsc_w_trabancie\n", " 0\n", "end\n", "```\n", "i zapyta o liczb\u0119 trabant\u00f3w:\n", "```ruby\n", "ile_potrzeba_samochodow?(10)\n", "```\n", "otrzyma b\u0142\u0105d:" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 11 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Przyk\u0142ad ten jest oczywi\u015bcie bardzo rozbudowany, ale je\u015bli przyjrzymy si\u0119 *stacktrace'owi*, to szybko mo\u017cemy odnale\u017a\u0107 miejsce, kt\u00f3re doprowadzi\u0142o do wyst\u0105pienia b\u0142\u0119du - przynajmniej po\u015brednio.\n", "\n", "Mo\u017cliwo\u015b\u0107 zidentyfikowania \u017ar\u00f3d\u0142a b\u0142\u0119du jest bardzo cenna, niemniej znacznie cz\u0119\u015bciej b\u0119dziemy chcieli, aby w takiej sytuacji program nadal dzia\u0142a\u0142. Co najwy\u017cej dopu\u015bcimy specjalny komunikat dla u\u017cytkownika informuj\u0105cy o b\u0142\u0119dzie. \n", "\n", "Aby zabezpieczy\u0107 si\u0119 przed wyj\u0105tkiem otaczamy \"kod specjalnej troski\" s\u0142owami kluczowymi `begin`, `rescue` i `end`:\n", "```ruby\n", "def oblicz_liczbe_pelnych_samochodow(liczba_sloni)\n", " begin\n", " liczba_sloni / liczba_miejsc_w_trabancie\n", " rescue ZeroDivisionError\n", " puts \"Nie dziel przez zero...\"\n", " 0\n", " end\n", "end\n", "ile_potrzeba_samochodow?(10)\n", "``` " ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 14 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Istotne pytanie, kt\u00f3re pojawia si\u0119 w tym kontek\u015bcie dotyczy **miejsca**, w kt\u00f3rym ma nast\u0105pi\u0107 obs\u0142uga b\u0142\u0119du. Tak jak widzimy w powy\u017cszym przyk\u0142adzie, obs\u0142uga wprost w funkcji `oblicz_liczbe_pelnych_samochodow` nie wystarczy\u0142a, poniewa\u017c ten sam b\u0142\u0105d wyst\u0119puje r\u00f3wnie\u017c w funkcji `obliczb_liczbe_niepelnych_samochodow`. Dlatego zdecydowanie lepszym miejscem obs\u0142ugi b\u0142\u0119du jest funkcja `ile_potrzeba_samochodow?`. \n", "\n", "```ruby\n", "# przywracamy poprzedni\u0105 definicj\u0119 tej funkcji\n", "def oblicz_liczbe_pelnych_samochodow(liczba_sloni)\n", " liczba_sloni / liczba_miejsc_w_trabancie\n", "end\n", "\n", "# oraz dodajemy obs\u0142ug\u0119 b\u0142\u0119du na wy\u017cszym poziomie\n", "def ile_potrzeba_samochodow?(liczba_sloni)\n", " begin\n", " puts \"Aby przewie\u017a\u0107 #{liczba_sloni} s\u0142owni potrzeba #{oblicz_liczbe_wszystkich_samochodow(liczba_sloni)} samochod\u00f3w.\"\n", " rescue ZeroDivisionError\n", " puts \"W trabancie nie mo\u017cna umie\u015bci\u0107 \u017cadnego s\u0142ownia, bo jest on ju\u017c\u00a0zajmowany przez myszy.\"\n", " end\n", "end\n", "ile_potrzeba_samochodow?(10)\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uda\u0142o nam si\u0119 upora\u0107 z trabantami, kt\u00f3re zosta\u0142y ju\u017c zaj\u0119te przez myszy. To nie s\u0105 jednak wszystkie mo\u017cliwe b\u0142\u0119dy, kt\u00f3re mog\u0105 si\u0119 pojawi\u0107. Jaki\u015b ma\u0142o rozgarni\u0119ty programista mo\u017ce np. wywo\u0142a\u0107 funkcj\u0119 w nast\u0119puj\u0105cy spos\u00f3b:\n", "```ruby\n", "ile_potrzeba_samochodow?(nil)\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 22 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oczywiste jest, \u017ce nie potrzeba \u017cadnego samochodu, je\u015bli w og\u00f3le nie mamy s\u0142oni do przewiezienia. Wywo\u0142anie powy\u017cszego kodu skutkuje jednak innym b\u0142\u0119dem ni\u017c wcze\u015bniej: `NoMethodError`. Oznacza on, \u017ce obiekt `nil` nie ma metody pozwalaj\u0105cej na jego dzielenie (zar\u00f3wno przez zero, jak przez dowoln\u0105 inn\u0105\u00a0warto\u015b\u0107). Problem ten rozwi\u0105\u017cemy r\u00f3wnie\u017c bezpo\u015brednio w funkcji `ile_potrzeba_samochodow?` dodaj\u0105c kolejn\u0105 klauzul\u0119 `rescue`:\n", "```ruby\n", "def ile_potrzeba_samochodow?(liczba_sloni)\n", " begin\n", " puts \"Aby przewie\u017a\u0107 #{liczba_sloni} s\u0142owni potrzeba #{oblicz_liczbe_wszystkich_samochodow(liczba_sloni)} samochod\u00f3w.\"\n", " rescue ZeroDivisionError\n", " puts \"W trabancie nie mo\u017cna umie\u015bci\u0107 \u017cadnego s\u0142ownia, bo jest on ju\u017c zajmowany przez myszy.\"\n", " rescue NoMethodError\n", " puts \"Trzeba poda\u0107 liczb\u0119 s\u0142oni a nie jak\u0105\u015b dziwn\u0105 warto\u015b\u0107: '#{liczba_sloni}' typu #{liczba_sloni.class}.\"\n", " end\n", "end\n", "ile_potrzeba_samochodow?(nil)\n", "ile_potrzeba_samochodow?(\"5\")\n", "ile_potrzeba_samochodow?(/aaaa/)\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 34 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wiemy ju\u017c jak obs\u0142ugiwa\u0107 wyj\u0105tki. A jak je zg\u0142asza\u0107? S\u0142u\u017cy do tego polecenie `raise`:\n", "```ruby\n", "raise \"Rzucam wyj\u0105tek bo tak.\"\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 36 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Je\u015bli nie podamy nazwy wyj\u0105tku, to domy\u015blnie b\u0119dzie to b\u0142\u0105d `RuntimeError`. Aby rzuci\u0107 wyj\u0105tek okre\u015blonego typu, musimy u\u017cy\u0107 konstruktora, kt\u00f3ry przyjmuje komunikat wyj\u0105tki jako parametr wywo\u0142ania:\n", "```ruby\n", "raise ArgumentError.new(\"Jaki\u015b dziwny ten argument\")\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 38 }, { "cell_type": "markdown", "metadata": {}, "source": [ "B\u0142\u0105d tego rodzaju obs\u0142ugujemy tak jak inne wyj\u0105tki:\n", "```ruby\n", "begin\n", " raise ArgumentError.new(\"Jaki\u015b dziwny ten argument\")\n", "rescue ArgumentError => ex\n", " puts ex\n", "end\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 40 }, { "cell_type": "markdown", "metadata": {}, "source": [ "W powy\u017cszym kodzie zosta\u0142a zostosowana dodatkowa mo\u017cliwo\u015b\u0107 przy obs\u0142udze wyj\u0105tk\u00f3w - przechwycenie ich do zmiennej, w tym wypadku `ex`. Zmienna taka zawiera wszystkie istotne informacje dotycz\u0105ce wyj\u0105tku, m.in. komunikat i stos wywo\u0142a\u0144:\n", "```ruby\n", "begin\n", " raise ArgumentError.new(\"Jaki\u015b dziwny ten argument\")\n", "rescue ArgumentError => ex\n", " puts ex\n", " puts ex.backtrace[0..5]\n", "end\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 44 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Zadanie 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zmodyfikuj funkcje `oblicz_liczbe_pelnych_samochodow` oraz `oblicz_liczbe_niepelnych_samochodow` tak by rzuca\u0142y wyj\u0105tek `ArgumentError`, je\u015bli liczba s\u0142oni jest mniejsza od 0. Nast\u0119pnie zmodyfikuj funkcj\u0119 `ile_potrzeba_samochodow?`, tak by obs\u0142ugiwa\u0142a ten wyj\u0105tek. W nast\u0119pnej kolejno\u015bci zmodyfikuj funkcj\u0119 `liczba_miejsc_w_trabancie`, tak by ponownie mo\u017cna by\u0142o wozi\u0107 s\u0142onie. Przetestuj g\u0142\u00f3wn\u0105 funkcj\u0119 przekazuj\u0105c jej r\u00f3\u017cne parametry (r\u00f3wnie\u017c ma\u0142o oczywiste). Czy tak zdefiniowana funkcja jest niezawodna?" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Obs\u0142uga plik\u00f3w" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Dwie podstawowe operacje na plikach to odczytywanie i zapisywanie danych do pliku. \n", "\n", "Obie operacje wymagaj\u0105 aby plik zosta\u0142 najpierw *otwarty*. S\u0142u\u017cy do tego metoda `File.open`, kt\u00f3ra wymaga podania \u015bcie\u017cki do pliku:\n", "```ruby\n", "plik = File.open(\"data/authors.csv\")\n", "puts plik.readline\n", "plik.close\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 48 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Po otwarciu pliku, mo\u017cna na nim wykonywa\u0107 operacje. W powy\u017cszym przyk\u0142adzie metoda `readline` wczyta\u0142a pierwsz\u0105 lini\u0119 pliku. Po jej wy\u015bwietleniu plik zosta\u0142 zamkni\u0119ty poleceniem `close`.\n", "\n", "Poniewa\u017c po otwarciu pliku trzeba go zawsze zamkn\u0105\u0107 (cho\u0107 programi\u015bci i programistki cz\u0119sto o tym zapominaj\u0105), Ruby oferuje alternatywn\u0105 sk\u0142adnie do obs\u0142ugi plik\u00f3w:\n", "```ruby\n", "File.open(\"data/authors.csv\") do |plik|\n", " puts plik.readline\n", "end\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Jej przewaga nad poprzedni\u0105 metod\u0105 jest taka, \u017ce po opuszczeniu bloku `do` ... `end` plik zostanie automatycznie zamkni\u0119ty. A je\u015bli zapomnieliby\u015bmy s\u0142owa `end`, to program si\u0119 po prostu nie wykona (wyst\u0105pi b\u0142\u0105d sk\u0142adni).\n", "\n", "Wy\u015bwietlenie 10 pierwszych wierszy wygl\u0105da nast\u0119puj\u0105co:\n", "```ruby\n", "File.open(\"data/authors.csv\") do |plik|\n", " 10.times do \n", " puts plik.readline\n", " end\n", "end\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 51 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Poniewa\u017c linie w pliku zako\u0144czone s\u0105 znakiem przej\u015bcia do nowej linii, warto pami\u0119ta\u0107 o zastosowaniu funkcji `chomp` przy wy\u015bwietlaniu poszczeg\u00f3lnych wierszy:\n", "```ruby\n", "File.open(\"data/authors.csv\") do |plik|\n", " 10.times do \n", " puts plik.readline.chomp\n", " end\n", "end\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 54 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Poszczeg\u00f3lne wiersze wczytywane za pomoc\u0105 polecenia `readline` s\u0105 po prostu \u0142a\u0144cuchami znak\u00f3w. Je\u015bli chcemy np. wy\u015bwietli\u0107 pierwsze pole w pliku, w kt\u00f3rym stosuje si\u0119 przecinek do oddzielenia p\u00f3l (format CSV), to mo\u017cemy zrobi\u0107 to nast\u0119puj\u0105co:\n", "```ruby\n", "File.open(\"data/authors.csv\") do |plik|\n", " 10.times do \n", " puts plik.readline.chomp.split(\",\").first\n", " end\n", "end\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 56 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Do obs\u0142ugi tego formatu istnieje osobna klasa `CSV`, kt\u00f3ra np. bierz\u0119 pod uwag\u0119, to, \u017ce przecinek mo\u017ce r\u00f3wnie\u017c by\u0107 tre\u015bci\u0105 pola. Ponadto obs\u0142uga plik\u00f3w `csv` za pomoc\u0105 tej klasy jest znacznie szybsza. Klasa ta nie jest jednak domy\u015blnie dost\u0119pna, dlatego trzeba za\u0142adowa\u0107 j\u0105 za pomoc\u0105 polecenia `require`. Ponadto wczytywanie kolejnych wierszy odbywa si\u0119 za pomoc\u0105 metody `shift`:\n", "```ruby\n", "require 'csv'\n", "CSV.open(\"data/authors.csv\") do |plik|\n", " 10.times do \n", " puts plik.shift.first\n", " end\n", "end\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 59 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Zapisywanie do pliku jest bardzo podobne do wy\u015bwietlania tre\u015bci na ekranie. Zapis do kolejnych linii realizowany jest za pomoc\u0105 polecenia `puts`. Jednak\u017ce pr\u00f3ba zapisu do nieistniej\u0105cego pliku ko\u0144czy si\u0119 pora\u017ck\u0105:\n", "```ruby\n", "File.open(\"data/moj_plik.txt\") do |plik|\n", " 1.upto(10) do |indeks|\n", " plik.puts \"To jest #{indeks} linia\"\n", " end\n", "end\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 62 }, { "cell_type": "markdown", "metadata": {}, "source": [ "W powy\u017cszym wywo\u0142aniu wyst\u0105pi\u0142 wyj\u0105tek `Errno::ENOENT`, oznaczaj\u0105cy, \u017ce plik, kt\u00f3ry chcieli\u015bmy otworzy\u0107 nie isnieje. Aby rozwi\u0105za\u0107 ten problem, konieczne jest zmodyfikowanie wywo\u0142ania funkcji `open`, przez dodanie flagi \"w\" informuj\u0105cej, \u017ce plik otwierany jest w trybie *do zapisu*:\n", "```ruby\n", "File.open(\"data/moj_plik.txt\",\"w\") do |plik|\n", " 1.upto(10) do |indeks|\n", " plik.puts \"To jest #{indeks} linia\"\n", " end\n", "end\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 64 }, { "cell_type": "markdown", "metadata": {}, "source": [ "To \u017ce kod faktycznie zadzia\u0142a\u0142 mo\u017cna zweryfikowa\u0107 zagl\u0105daj\u0105c do pliku `data/moj_plik.txt`." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Zadanie 2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Napisz program, kt\u00f3ry przepisze wszystkie pierwsze pola (czyli nazwiska autor\u00f3w) z pliku `data/authors.csv` do pliku `data/names.txt`. W nazwiskach znaki podkre\u015blenia maj\u0105 zosta\u0107 zast\u0105pione spacjami. Aby odwiedzi\u0107 wszystkie wiersze w pliku u\u017cyj metody `each`. Zwr\u00f3\u0107 uwag\u0119, \u017ce plik do kt\u00f3rego zapisujesz i plik kt\u00f3ry odczytujesz musz\u0105 by\u0107 dost\u0119pne za pomoc\u0105 innych zmiennych (przujmuj\u0105c, \u017ce pracujemy na obu plikach jednocze\u015bnie) oraz, \u017ce w pliku `authors.csv` jest wi\u0119cej pozycji ni\u017c 10." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 70 }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Zadanie 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wykonaj to samo zadanie co w poprzednim \u0107wiczeniu, uwzgl\u0119dniaj\u0105c nast\u0119puj\u0105ce r\u00f3\u017cnice:\n", "* dane maj\u0105 trafi\u0107 do pliku `data/sorted_names.txt`,\n", "* dane maj\u0105 by\u0107 posortowane zgodnie z porz\u0105dkiem alfabetycznym.\n", "\n", "Mo\u017cesz pomi\u0105\u0107 fakt, \u017ce w pliku na pierwszym miejscu pojawia si\u0119 imi\u0119, a nie nazwisko autora." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 73 } ], "metadata": {} } ] }