background image

1

Wykład VIII

Języki programowania

Algorytmy i struktury danych
Wyższa Szkoła Biznesu
Semestr  III    Informatyka 
stosowana

background image

2

Interesuje nas 

wykonywanie algorytmów 

przy pomocy komputera

Naszym 

największym 

zainteresowaniem  są  jednak  objęte 
algorytmy  przeznaczone  do  wykonania 
przez  komputery,  a  zatem  powinniśmy 
poświęcić  nieco  czasu  na  odniesienie 
algorytmów 

do 

prawdziwych 

komputerów.

background image

3

Komputer potrafi zwykle 

wykonać niewielką ilość 

prostych operacji

Nawet  najbardziej  wyrafinowany  komputer  jest  w 

rzeczywistości  tylko  ogromną,  dobrze  zorganizowaną 

masą bitów i, co więcej, normalnie potrafi on wykonywać 

na  nich  jedynie  niewielką  liczbę  krańcowo  prostych 

operacji, 

takich 

jak 

zerowanie, 

przerzucanie 

i sprawdzanie.
W  jaki  sposób  przedstawiamy  algorytm  prawdziwemu 

komputerowi  i  jak  sprawiamy,  że  wykonuje  on 

odpowiedni proces tak, jak to zamierzono?
Powiedzmy  to  jeszcze  bardziej  bezceremonialnie.  Jak 

sprawiamy,  że  głupia  maszyna  o  tak  małych 

zdolnościach  w  imponujący  sposób  realizuje  nasze 

subtelne i starannie opracowane algorytmy?

background image

4

Algorytmy muszą być 

zapisane w jednoznaczny i 

formalny sposób

Pierwszym  spostrzeżeniem  jest  to,  że  nasze  algorytmy 

muszą  być  zapisane  w jednoznaczny  i  formalny 

sposób
Zanim  spróbujemy  zrozumieć,  w  jaki  sposób  obracając 

bitami  można  wypełnić  proste  zadanie,  np.  „przebiegnij 

listę,  dodając  każdą  płacę  do  liczby  «na  boku»”,  musimy 

zadanie to dokładnie określić odpowiadając na pytania:

– Gdzie komputer znajdzie tę listę?
– Jak ją przebiegnie?
– Gdzie znajdzie płacę i liczbę ,,na boku”? Itd.

Zadanie  „przebiegnij  listę”  nie  jest  wcale  wiele  bardziej 

dokładne  i  jednoznaczne  niż  „ubijaj  białka  aż  do 

spienienia”.

background image

5

Języki programowania, 

programy, programiści

Aby  opisać  algorytm  na  użytek  komputera, 
posługujemy  się  językiem  programowania,  
którym piszemy programy.
Program
 to oficjalne i formalne wydanie algorytmu, 
nadające się do wykonania przez komputer.
Język  programowania  składa  się  z notacji  i  reguł, 
według których pisze się programy, a osoba pisząca 
program zwie się programistą. (Ta osoba nie musi 
być  autorem  algorytmu,  na  którym  opiera  się 
program.)

background image

6

Programy wymagają 

precyzyjnej składni

Język  programowania  wiąże  się  zwykle  ze  sztywną  składnią, 

dopuszczającą używanie jedynie specjalnych kombinacji wybranych 

symboli i słów kluczowych.
Każda próba nadużycia tej składni może okazać się fatalna: jeśli np. 

napisze się wczytaj X w języku, którego instrukcje wczytywania są 

postaci 

wprowadź 

X

możliwe, 

że 

wynikiem 

będzie 

bezceremonialne  BŁĄD  SKŁADNIOWY  E5414  W  WIERSZU  108.  To 

oczywiście  wyklucza  nawet  tak  uprzejme,  ale  nieprecyzyjne 

żądania, jak „proszę wczytać wartość X z wejścia”.
To prawda,  że  uprzejme,  obszerne  instrukcje  są  przyjemniejsze  dla 

ludzi i być może są dla nich bardziej jednoznaczne niż ich zwięzłe i 

bezosobowe odpowiedniki; jest także prawdą, że chcielibyśmy, aby 

komputery  były  tak  przychylne  użytkownikowi,  jak  to  tylko  jest 

możliwe.  Jednakże  trzeba  przeciwstawić  te  fakty  aktualnemu 

brakowi  zdolności  rozumienia  przez  komputery  swobodnie 

mówionego (lub pisanego) języka naturalnego.

background image

7

Co zawiera formalna 

składnia języka 

programowania ?

Formalna  składnia  typowego  języka  programowania 

zawiera:

– systematyczne warianty kilku struktur sterujących,
– systematyczne  sposoby  definiowania  rozmaitych 

struktur danych 

– systematyczne wzorce podstawowych instrukcji. 

Muszą  one  należeć  do  zbioru  dopuszczalnych 

instrukcji 

stosowanego 

języka 

programowania, 

ponieważ  komputer  nie  wykona  żadnej  instrukcji, 

nawet najbardziej jasnej i jednoznacznej, jeśli nie ma 

jej wśród tych, które dopuszcza dany język.

background image

8

Przykład

Algorytm sumowania liczb 

od 1 do N

W  typowym  (hipotetycznym) 

języku 

programowania 

JP 

można 

by 

zapisać, 

jak 

następuje:

wczytaj N;
X:=0;
dla od 1 do N wykonaj
X:=X+Y
koniec; wypisz X.

X:=0 

jest 

instrukcją 

przypisania,  która  nadaje 

zmiennej 

X 

wartość 

początkową  0  (w algorytmach 

zapisywaliśmy ją jako X←0).

Zwróć  uwagę  na  słowa  kluczowe 

zapisane grubszą czcionką; są one 

częścią składni JP
Oznacza  to,  że  formalna  definicja 

składni 

najprawdopodobniej 

zawiera klauzulę stwierdzającą, że 

jednym  z  poprawnych  rodzajów 

instrukcji  w  tym  języku  jest 

instrukcja-dla

której 

ogólny 

wzorzec  składa  się  ze  słowa 

kluczowego 

dla, 

po 

czym 

następuje  poprawny  nagłówek-

dla,  po  czym  następuje  wykonaj, 

po  czym  następuje  poprawna 

instrukcja,  po  czym  następuje 

koniec

background image

9

Notacja BNF (Backus-Naur 

Form, od nazwisk 

wynalazców)

Definicje składni można zapisać symbolicznie w notacji 

zwanej BNF w następujący sposób (,,|” oznacza ,,lub”):

<instrukcja>:<instrukcja-dla>|<instrukcja-

przypisania>|...
<instrukcja-dla>: 

dla  <nagłówek-dla> 

wykonaj 

<instrukcja> koniec
Opis klauzuli nagłówka mógłby brzmieć:

<nagłówek-dla>:  <zmienna>  od  <wartość>  do 

<wartość>

<wartość> można wtedy określić jako zmienną, jawnie 

podaną liczbę lub jakiś inny obiekt.

background image

10

Schematyczny sposób 

przedstawiania reguł 

składniowych

background image

11

Schematyczny sposób 

przedstawiania reguł 

składniowych

background image

12

Podobnie są narzucane 

wzorce definiowania 

struktur danych

 Np. tablicę TA o wymiarach 50 na 100, której wartości 

mogą być liczbami całkowitymi, można by zdefiniować 

w hipotetycznym języku JP instrukcją

definiuj TA tablica [1..50, 8..107] w niej liczby 

całkowite

a  język  mógłby  dopuścić  odwołania  do  elementów 

tablicy TA wyrażeniami postaci

TA(wartość, wartość)

Zauważ,  jak  określiliśmy  nie  tylko  wymiary  TA,  lecz 

także  jej  wartości  indeksów;  wiersze  tablicy 

ponumerowano  1,  2,  ...,  50,  ale  jej  kolumnom  (z 

jakiegoś powodu) nadano numery 8, 9, ..., 107.

background image

13

Składnia wyznacza 

znacznie więcej

W  rzeczywistości  składnia  wyznacza  znacznie 

więcej  niż  tylko  dostępne  struktury  sterujące  i 

struktury danych oraz operacje elementarne.
Zwykle  język  ma  sztywne  reguły  nawet  w  tak 

drobnych  kwestiach,  jak  poprawne  ciągi  symboli, 

których  można  używać  do  nazywania  zmiennych 

lub  struktur  danych,  i  maksymalna  liczba  cyfr 

dopuszczalna w wartościach numerycznych.
Niektóre 

języki 

ograniczają 

długości 

nazw 

zmiennych  do  najwyżej,  powiedzmy,  ośmiu 

symboli, z których pierwszy ma być literą alfabetu.

background image

14

Ważna jest także sprawa 

interpunkcji

W językach programowania nie należy do rzadkości 
wymaganie,  aby  po  pewnych  rodzajach  instrukcji 
następował  średnik,  a  po  innych  -  odstęp,  aby 
komentarze były ujęte w specjalne nawiasy, itd.
Biada  tym,  którzy  do  tych  wymagań  się  nie 
zastosują  (dopadnie  ich  zapewne  „choroba 
średnika”).
Niedługo  poznamy  szczegółowy  opis  składni 
konkretnego  języka  programowania,  z  regułami 
stosowania średników i całą resztą.

background image

15

Programy wymagają 

precyzyjnej semantyki

Wyposażenie  języka  w  precyzyjną  składnię  stanowi 

tylko  część  zadania  stojącego  przed  projektantem 

języka programowania.
Bez formalnej i jednoznacznej semantyki a więc bez 

znaczenia 

każdego 

wyrażenia 

dozwolonego 

składniowo, składnia jest niemal bezwartościowa.
Gdyby  nie  podano  znaczenia  instrukcji,  w  naszym 

hipotetycznym języku JP taki oto fragment programu

dla Y od 1 do N wykonaj

mógłby, oznaczać „odejmij Y od 1 i zapamiętaj wynik 

N”.

background image

16

Bez reguł semantycznych 

nie poznamy 

przeznaczenia instrukcji

Co  gorsza,  któż  powiedział,  że  słowa  kluczowe  dla

od,  do  itp.  mają  mieć  coś  wspólnego  z  ich 

znaczeniem w konwencjonalnej polszczyźnie?
A może ten sam fragment programu oznacza „wyzeruj 

całą  pamięć  komputera,  zmień  wartości  wszystkich 

zmiennych  na  0,  wypisz  »DO  DIABŁA  Z  JĘZYKAMI 

PROGRAMOWANIA« i zatrzymaj się!”?
Któż  powiedział,  że  „:=”  oznacza  „przypisz”  i  że  „+” 

oznacza  „plus”?  Któż  powiedział,  że  instrukcje 

wykonuje się w takiej kolejności, w jakiej są napisane? 

A  może  „;”  oznacza  „po  wykonaniu  tego,  co 

następuje”, nie zaś ,,a potem wykonaj, co następuje”?

background image

17

Na szczęście w językach 

znaczenia symboli są podobne do 

znaczeń w języku angielskim

Oczywiście  moglibyśmy  odgadnąć,  co  to  oznacza, 

ponieważ projektant języka JP tak dobrał zapewne słowa 

kluczowe  i  symbole  specjalne,  aby  ich  znaczenia  były 

możliwie najbardziej zbliżone do ogólnie przyjętych.
Ale komputer na podstawie takich przypuszczeń działać 

nie  może.  Gdyby  nawet  mógł  w  jakiś  sposób 

wywnioskować  konwencjonalne  znaczenie  słów  i  takich 

symboli,  jak  „+”,  skąd  wiedziałby,  bez  wyraźnego 

pouczenia, co zrobić z nagłówkiem pętli

dla Y od 1 do N wykonaj

gdy wartością N jest powiedzmy -314.1592?

background image

18

W poznawaniu składni i 

semantyki języka wspiera 

nas dokumentacja

Wydaje 

się 

zatem 

jasne, 

że 

język 

programowania  wymaga  nie  tylko  sztywnych 
reguł 

definiowania 

postaci 

poprawnego 

programu,  ale  także  równie  sztywnych  reguł 
definiowania jego znaczenia.
Projektanci  i  producenci  dostarczają  jednak 
szczegółową  dokumentację  języka,  całe  jej 
tomy.  Te  podręczniki  języka  zawierają 
bogactwo  informacji  dotyczących  zarówno 
składni, jak i semantyki.

background image

19

Przystępujemy do 

kodowania

Mamy  zatem  język  programowania  JP,  składnię, 

semantykę  i  całą  resztę,  i  właśnie  skończyliśmy 

opracowywanie  algorytmu  A  rozwiązania  pewnego 

zadania algorytmicznego.
Przystępujemy  teraz  do  zaprogramowania  tego 

algorytmu  w  języku JP, czynności określanej  niekiedy 

jako  kodowanie  A  w  JP,  i  chcemy  przedstawić 

powstały  program,  nazwijmy  go  A

P

  naszemu 

komputerowi.
Cóż dzieje się dalej? Odpowiedź brzmi, że w zasadzie 

istnieją  dwie  możliwości,  zależnie  od  tego,  jakiego 

języka i komputera użyjemy.

background image

20

Od algorytmu do języka 

maszynowego

Program  A

P

  możemy  wprowadzić  do  pamięci  komputera 

wystukując  go  na  klawiaturze,  wczytując  z  urządzenia 

takiego  jak  dysk,  odbierając  z  innego  komputera  za 

pośrednictwem elektronicznego kanału komunikacyjnego lub 

w  jakiś  inny  sposób  (same  nośniki  fizyczne  i  sposób  ich 

używania,  nas  tutaj  nie  interesują,  w przeciwieństwie  do 

tego, co dzieje się potem).
Program  A

P 

przechodzi  pewną  liczbę  przekształceń,  które 

stopniowo  sprowadzają  go  „w  dół”  do  poziomu,  na  którym 

komputer  już  może  sobie  poradzić.  Ostatecznym  wynikiem 

tych  przekształceń  jest  program  A

M

  na  poziomie  maszyny 

(mówi  się  o  nim  również,  że  jest  napisany  w  języku 

maszyny), gdyż już teraz komputer „rozumie” jego operacje 

podstawowe, takie jak instrukcje manipulowania bitami.

background image

21

Języki programowania 

wysokiego poziomu

Liczba  przekształceń  bywa  różna,  zależnie  od  języka  i 

komputera, ale najczęściej jest ich od dwóch do czterech. 
Obiektami pośrednimi  są zwykle poprawne programy  w coraz 

to  prymitywniejszych  językach  programowania.  Ich  repertuar 

struktur sterujących i struktur danych stopniowo, podobnie jak 

instrukcji  podstawowych,  staje  się  coraz  skromniejszy,  aż 

wreszcie 

pozostają 

jedynie 

najbardziej 

elementarne 

możliwości działania na bitach.
Dopiero po tej ostatecznej fazie przekształceń komputer może 

uruchomić (lub wykonać) pierwotny program A

M

.

Jest  już  dla  nas  teraz  oczywiste,  dlaczego  języki  takiego 

rodzaju  jak  ten,  w  którym  napisano  pierwotny  program 

użytkownika  A

P

,  nazywają  się  językami  programowania 

wysokiego poziomu

background image

22

Przypomina to 

podprogramy

Przekształcenia, 

których 

mówiliśmy 

przypominają  nieco  zastępowanie  procedur  ich 
treścią.
Na 

instrukcję 

języku 

programowania 

wysokiego  poziomu  można  patrzeć  jak  na 
wywołanie  podprogramu  lub  jak  na  instrukcję 
podstawową,  która  jednak  dla  komputera  nie 
jest dostatecznie podstawowa.
W  konsekwencji  trzeba  ją  uściślić  i  sprowadzić 
do poziomu kompetencji komputera.

background image

23

Kompilacja, asembler

Pomówmy o pierwszym przekształceniu, zwanym kompilacją, 

którym  program  wysokiego  poziomu  A

P

  zostaje  przetłumaczony 

na  program  A

S

  w  języku  niższego  poziomu,  zwanym  językiem 

adresów symbolicznych lub asemblerem.
Języki adresów symbolicznych różnią się dla różnych maszyn, ale 

z  reguły  stosuje  się  w  nich  dość  proste  struktury  sterujące, 

przypominające  instrukcje  skocz  oraz  konstrukcje  jeśli-to; 

operują  nie  tylko  bitami,  lecz  także  liczbami  całkowitymi  i 

ciągami symboli.
Mogą  one  odwoływać  się  bezpośrednio  do  adresów  w  pamięci 

komputera (ogromnego obszaru zawierającego całe stosy bitów), 

mogą  odczytywać  spod  tych  adresów  wszelkie  zakodowane 

liczby i napisy, i mogą tamże zapamiętywać kody liczb i napisów.

background image

24

Przykład (hipotetycznego) 

asemblera

Typową konstrukcję pętli wysokiego poziomu:

dla Y od 1 do N wykonaj
   <treść pętli>  koniec

można  przetłumaczyć  na  jej  odpowiednik  w  języku  adresów 

symbolicznych:

LDS 0,Y

(załaduj stałą 0 pod adres Y)

PĘTLA POR N, Y

(porównaj wartości pod adresami N i Y)

SKR RESZTA(jeśli równe, skocz do instrukcji o etykiecie RESZTA)
DDS 1,Y

(dodaj stałą 1 do wartości pod adresem Y)

    <przetłumaczona treść pętli>
SKO PETLA (skocz z powrotem do instrukcji o etykiecie PĘTLA)

RESZTA

(reszta programu)

background image

Przekształcanie 

programu 

wysokiego 

poziomu na kod 

maszynowy

Proces  kompilacji,  tłumaczący 

programy 

komputerowe 

wysokiego  poziomu  na  język 

adresów  symbolicznych  sam 

jest również wykonywany przez 

program 

komputerowy 

(kompilator)
Proces 

przekształcania 

na 

niższy 

poziom, 

którym 

kompilacja  jest  pierwszą  i 

najbardziej 

zawiłą 

częścią, 

ilustruje rys.
Pozostałe 

kroki 

tłumaczą 

asembler na język maszynowy. 

background image

26

Oprogramowanie 

systemowe

Warto  zaznaczyć,  że  kompilatory  różnych  języków 

wysokiego  poziomu,  jakie  obsługuje  dany  komputer, 

często  (choć  nie  zawsze)  należą  do  dużej  grupy  różnych 

programów  dostarczanych  przez  producenta  (komputera 

bądź 

systemu 

operacyjnego), 

zwykle 

zwanych 

oprogramowaniem systemowym.
Ich  ogólną  rolą  jest  ułatwianie  współpracy  z  komputerem 

na  wysokim  poziomie  i  jednoczesne  odgrodzenie 

użytkownika  od  wielu  szczegółów  niskiego  poziomu 

związanych  z  trybem  współpracy,  np.  uruchomieniem 

programów 

napisanych 

przez 

użytkownika, 

komunikowaniem się z innymi komputerami, sprzęganiem 

się ze specjalnym wyposażeniem zewnętrznym...

background image

27

Interpretatory i 

natychmiastowe 

wykonanie

Jest  jeszcze  inny  sposób,  w  jaki  komputery  wykonują 

przedstawione  im  programy,  który  nie  obejmuje 

tłumaczenia całego programu na język niższego poziomu.
Każda 

z instrukcji 

wysokiego 

poziomu 

zostaje 

przetłumaczona 

na 

instrukcje 

poziomu 

maszyny 

natychmiast  po  jej  napotkaniu,  a  te  z  kolei  od  razu  się 

wykonuje.  W  pewnym  sensie  interpreter  pełni  rolę 

„pseudoprocesora”  wykonując  instrukcje  wysokiego 

poziomu jedną po drugiej, dokładnie tak, jak są podane. 
Mechanizm  odpowiedzialny  za  to  lokalne  tłumaczenie  i 

natychmiastowe 

wykonanie 

jest 

też 

fragmentem 

oprogramowania 

systemowego, 

nazywanym 

interpretatorem (interpreterem).

background image

28

Zalety i wady 

interpretowania 

programów

Podejście interpretacyjne ma pewne oczywiste zalety, m.in.

zwykle  łatwiej  napisać  byle  jaki,  ale  użyteczny 

interpretator niż sensowny kompilator;
wykonywanie  prowadzone  przez  interpretator  pozwala  na 

łatwiejsze śledzenie i podsumowanie wykonania programu.

Są  jednak  cechy  interpretacji  niekorzystne  w  stosunku  do 

kompilacji
nie ma zwartego pliku wykonywalnego
trzeba  rozpowszechniać  kod  źródłowy  (trudno  ochronić 

prawa autorskie)
wolniejsze  wykonanie  (każdorazowo  trzeba  dokonywać 

interpretacji)

background image

29

Kompilować czy 

interpretować ?

To,  czy  komputer  będzie  kompilował,  czy 
interpretował  dany  program,  zależy  od 
przeznaczenia  danego  programu,  samego 
komputera, 

języka 

programowania, 

konkretnego 

pakietu 

oprogramowania 

systemowego, którego używamy.
Niektóre  języki  programowania  nadają  się  do 
interpretowania  bardziej  niż  inne,  wszystkie 
jednak mogą w zasadzie być kompilowane.

background image

30

Dlaczego nie 

algorytmiczne Esperanto?

Od czasu, gdy we wczesnych latach pięćdziesiątych 

zaczęły 

pojawiać  się  języki  programowania 

wysokiego  poziomu,  zaprojektowano  ich  setki  oraz 

napisano dla nich kompilatory i (lub) interpretatory, 

a  nadal  nowe  języki  pojawiają  się  jak  grzyby  po 

deszczu.
Dlaczego?  Czynie  byłoby  lepiej  mieć  jeden  język 

uniwersalny,  takie  algorytmiczne  Esperanto,  w 

którym  zapisywałoby  się  wszystkie  algorytmy  i 

którego  każdy  mógłby  się  łatwo  nauczyć?  Skąd 

taka  mnogość  języków?  Czym  się  różnią  ?  Kto 

używa których języków i w jakich celach?

background image

31

Dlaczego nie 

algorytmiczne Esperanto? 

Odpowiedź

Zasadniczo są dwie przyczyny tego zjawiska:

– nowe  osiągnięcia  sprzętowe  (choć  dziś  w  dużej 

mierze  udaje  się  uwolnić  wiele  języków  od 
bezpośredniego, ścisłego związku ze sprzętem).

– oraz nowe i zróżnicowane dziedziny zastosowań

;

Obie  stwarzają  zapotrzebowanie  na  nowe  języki 
programowania. 

Badania 

nad 

językami 

programowania  należą  do  z  najbardziej  się 
rozwijających  i przyciągających  powszechną  uwagę 
dziedzin informatyki.

background image

32

Badania nad językami 

programowania

Ludzie  chcą  języków  zarówno  uniwersalnych,  jak  i  przeznaczonych 

do szczególnych zastosowań oraz ich precyzyjnej definicji i sprawnej 

realizacji.
Wymyśla się wyrafinowane techniki kompilacji i tłumaczenia, proponuje 

się  nowe  struktury  sterujące  i  nowe  struktury  danych  oraz  wprowadza 

się  silne  mechanizmy  umożliwiające  programistom  definiowanie  ich 

własnych struktur danych.
Pożądane  są  języki  zachęcające  do  dobrego  stylu  programowania 

i wszyscy  próbują  zaprojektować  je  tak,  aby  zawrzeć  w  nich  rozmaite 

zalecane idee budowy algorytmów.
Badacze 

interesują 

się 

opracowaniem 

środowisk 

programistycznych,  czyli  przychylnych  użytkownikowi  systemów 

konwersacyjnych,  pozwalających  programiście  pisać,  redagować, 

zmieniać, wykonywać, analizować, poprawiać i symulować programy.
Fascynujące  badania  dotyczą  wizualnych  języków  programowania

umożliwiających rysowanie programów zamiast ich pisania.

background image

33

Uniwersalność języków 

programowania

Ostatnia  nasza  uwaga  dotyczy  uniwersalności  języków 

programowania.
W pewnym technicznym sensie praktycznie wszystkie języki 

programowania  są  równoważne  pod  względem  swojej  siły 

wyrazu.  Każde  zadanie  algorytmiczne  rozwiązywalne  w 

jednym  języku  jest  w  zasadzie  rozwiązywalne  również  w 

każdym innym języku.
Różnice  między  językami  są  pragmatyczne  i  dotyczą 

przydatności  do  pewnych,  zastosowań,  przejrzystości, 

struktury,  sprawności  realizacji  i  algorytmicznych  sposobów 

myślenia.
Wobec  znaczących  różnic  między  językami  programowania 

ten fakt może być przyjmowany jako coś niespodziewanego. 


Document Outline