ԴԱՍԸՆԹԱՑՆԵՐԻ ՄԱՏՅԱՆ
Դասընթացներին վերաբերվող նյութեր և նշումներ

Table of Contents

ԳԼԽԱՎՈՐ

Էջի մասին

Այս էջում հրապարակվելու են դասընթացների վերաբերյալ բոլոր նյութերը, հղումները և պրակտիկ աշխատանքները՝ դասընթացների անցկամանը զուգահեռ։

PowerPoint ձևաչափով սլայդերը կարող եք դիտել նաև վեբ֊հավելվածի օգնությամբ։ Հավելվածից օգտվելու համար հարկավոր է ունենալ Microsoft հաշիվ (account):

Հետադարձ կապ
info@ingenium.am - ընդհանուր հարցերի համար
lmelikyan@ingenium.am - դասընթացների բովանդակությանը վերաբերող հարցերի համար

ԴԱՍԸՆԹԱՑՆԵՐ

1. Տեղեկատվական տեխնոլոգիաներ

1.1 Ծանոթություն

1.2 Տեղեկատվություն կամ ինֆորմացիա

[2020-07-08]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - Analytical Engine

1.3 Վերացականություն (Abstraction)

[2020-07-10]

Ներկայացում (Google Slides)

Պարզաբանում
Ռեկուրսիան, դիտարկելով միայն որպես վերացկանություն (ընդհանուր հասկացություն), չի արտացոլում Համակարգչային գիտության (Computer Science) մեջ կիրառական կարևորությունն ու լայն հնարավորությունները։ Այդ իսկ պատճառով՝ Լեզվական շարահյուսություն (1.6), Տվյալներ (1.8) և Ալգորիթմներ (1.9) դասընթացների ընթացքում առանձին դիտարկվելու են ռեկուրսիայի համապատասխան կիրառական մոդելները։
Հետևյալ արխիվային ֆայլը [ droste.zip ] կիրառական որևէ արժեք չի ներկայացնում, բայց, որպես ռեկուրսիվ տվյալի իրական իմպլեմենտացիա, հետաքրքիր կարող է լինել ուսումնասիրության տեսանկյունից։

  • Վիզուալ հաշվիչ՝ տարբեր հիմքով թվերի զուգահեռ արտապատկերմամբ։
  • Հղում ծանոթության համար - ASCII, Unicode

1.4 Սարքավորումներ (Hardware)

[2020-07-13]

Ներկայացում (Google Slides)

Հետևյալ հղումներով կարելի է տեսնել MOS 6502 պրոցեսորի աշխատանքի վիզուալ սիմուլյացիան`

  1. Visual Transistor-level Simulation - ARM1
  2. Visual Transistor-level Simulation - 6502
  • Հղում ծանոթության համար - RAID

1.5 Ծրագրային միջավայր (Software)

[2020-07-15]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - POSIX, UNIX

1.6 Շարահյուսություն (Syntax)

[2020-07-17]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - Polish notation

1.7 Իմաստաբանություն (Semantics)

[2020-07-20]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - Lambda Calculus

1.8 Տվյալներ (Data)

[2020-07-22]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - XML, JSON, YAML

1.9 Ալգորիթմներ

[2020-07-24]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - Regular Expression

1.10 Ծրագրավորում

[2020-07-27]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - JIT compilation

1.11 Ցանց (Network)

[2020-07-29]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - CDN (Content Delivery Network)

1.12 Արտադրական պրոցես

[2020-07-31]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - ERP (Enterprise Resource Planning)

2. Համակարգչային գրաֆիկայի հիմունքներ

2.1 Ներածական

[2020-08-03]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - PostScript, TeX, LaTeX

2.2 Տարածական և գունային մոդելներ

[2020-08-05]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - Checker shadow illusion, Chubb illusion

2.3 Ռաստերային գրաֆիկա

[2020-08-07]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - Gaussian blur

2.4 Վեկտորային գրաֆիկա

[2020-08-10]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - CNC (Computer Numerical Control)

2.5 Եռաչափ գրաֆիկա

[2020-08-12]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - Normal Mapping

2.6 Փոխարկում (Conversion)

[2020-08-14]

Ներկայացում (Google Slides)

  • Հղում ծանոթության համար - 3D scanning

3. Ծրագրավորում

3.1 Միջավայր

[2020-08-31]

  • Environment
    • Path - հասցե (հասկացողություն)

      Ֆայլային համակարգի հասցեավորման ձևաչափ, որով բնորոշվում է ֆայլի եզակի հասցեն։

      Տես՝ Path - փոփոխական

      Յուրաքանչյուր հասցե, ըստ POSIX ստանդարտի, պարունակում է առնվազն երկու «ենթահասցե» (sub-directory)` . - current directory և .. - parent directory: Հասցեն կարող է լինել ամբողջական (absolute-path) և հարաբերական (relative-path)` որևէ հասցեի նկատմամբ (լռելյայն՝ current directory):
      Հասցեավորման առանձնահատկությունները տարբերվում են՝ կախված օպերացիոն համակարգի և ֆայլային համակարգի համադրությունից.

        UNIX-like Windows
      Ելակետ (root) / <letter>:
      Բաժանիչ / \ (/ - մուտք)
      Case sensitive insensitive
      Նշաններ բացառությամբ / բացառությամբ /\:*”?<>
      Օրինակներ / (root), /usr/bin, ../../home D:\ (root), C:\Users\Public, ../Users/Public

      DOS/Windows ՕՀ֊երում յուրաքանչյուր կրիչ ունի սեփական ելակետային հասցե:

      Գրեթե բոլոր ժամանակակից ֆայլային համակարգերը Unicode համատեղելի են և case-sensitive` բացառությամբ օպերացիոն համակարգի առանձնահատկությունների (օրինակ՝ DOS / Windows)։

      Հարաբերական (relative) հասցեներն, ի տարբերություն ամբողջական (absolute) հասցեների, կարող են չներառել ելակետային հասցեն և «հիմք» ընդունել կատարման ենթակա ֆայլի հասցեն, ինչն ապահովում է միևնույն հասցեավորման կառուցվածի համատեղելիությունը տարբեր համակարգերում։ Օրինակ՝ ../files/ կամ ./images/pic.jpg:

    • Environment variables

      Օպերացիոն համակարգի փոփոխականներ, որոնք կրում են կանխորոշված արժեքներ՝ որպես հասցե կամ հատուկ կարգավորում, և հասանելի են ՕՀ֊ում կատարման ենթակա բոլոր պրոցեսներին։

      Variable Purpose
      PATH կատարման ֆայլերի (executables) տեղակայման հասցեների ցանկ՝ ըստ հաջորդական նախապատվության
      HOME օգտատիրոջ (user) ելակետային հասցե (home), Windows ՕՀ֊ում՝ USERPROFILE
      TEMP,TMP ժամանակավոր ֆայլերի տեղակայման հասցե
      LANG ռեգիոնալ (locale) կարգավորումներ` լեզու, չափման միավորներ, և այլ,
      etc.  

      Windows ՕՀ֊ի միջավայրի փոփոխականները գտնվում են հետևյալ կարգավորման պատուհանում՝ Control Panel - System - Advanced system settings (ձախ մենյուից) - Environment Variables... (ներքևի աջ անկյունում)։ Համապատասխան կարգավորումները հարկավոր է կատարել System variables դաշտում։

      Փոփոխականների արժեքները կարելի է ստանալ հղում հատուկ ձևաչափով, օրինակ՝ $PATH (UNIX-like), %PATH% (DOS/Windows)։

  • Terminal - CLI

    Հրամանների տողի միջերեսը (Command-Line Interface) կամ տերմինալը, ըստ POSIX ստանդարտի, օպերացիոն համակարգի ֆունկցիոնալի հետ փոխհամագործակցության միջերես է։

    DOS և GNU/Linux ՕՀ֊երում տերմինալն առաջնային միջերեսն է։
    MacOS ՕՀ֊երում կա հատուկ հավելված` Termial:
    Window ՕՀ֊երում տերմինալի դեր է կատարում cmd հավելվածը։

    • Տերմինալի աշխատանքը

      Տերմինալի հետ փոխհամագործակցությունը տեղի է ունենում.

      1. հրամանների կամ հավելվածների (եթե վերջինների հասցեները պահպանված են Path փոփոխականում) և դրանց արգումենտների ներմուծմամբ,
      2. արդյունքի արտաբերմամբ, եթե այդպիսին նախատեսվում է, կամ սխալների պարագայում դրանց արտաբերմամբ։

      DOS/Windows ՕՀ֊երի հրամանների ցանկը կարելի է ստանալ help հրամանի օգնությամբ (առանց արգումենտների)։

      Տերմինալում կատարման ընթացքը դադարեցնելու կամ հրամանը չեղարկելու համար հարկավոր է ստեղնաշարի օգնությամբ կատարել Ctrl-C հրամանը։

      Օրինակներ․

      Help and docs

      # UNIX-like
      <command> --help
      man <command>
      
      # Windows
      <command> /?    # on Windows/DOS native commands only
      help <command>
      

      Get current (working) directory

      # UNIX-like
      pwd             # print working directory
      
      # Windows
      cd              # print working directory with no-args only
      

      List files and directories

      # UNIX-like
      ls              # list files and directories
      ls -l           # list as long listing
      ls -la          # list all (+ hidden) as long listing
      
      # Windows
      dir             # list files and directories
      
      -- UNIX-like output (ls -l) --
      
      drwxr-xr-x 5 user users  4096 2020-08-31 01:26 .
      drwxr-xr-x 4 user users  4096 2020-08-31 01:05 ..
      drwxr-xr-x 3 user users  4096 2020-07-27 14:10 counter
      drwxr-xr-x 2 user users  4096 2020-07-11 00:02 files
      drwxr-xr-x 4 user users  4096 2020-05-31 19:15 styles
      -rw-r--r-- 1 user users 36205 2020-08-31 01:26 Docs.org
      -rw-r--r-- 1 user users   645 2020-07-26 23:55 counter.html
      -rw-r--r-- 1 user users 17757 2020-05-29 00:22 example.html
      -rw-r--r-- 1 user users   766 2020-05-26 21:17 favicon.ico
      -rw-r--r-- 1 user users 70634 2020-08-31 01:10 index.html
      
      -- Windows output (dir) --
      
      2020-08-31  22:41    <DIR>          .
      2020-08-31  22:41    <DIR>          ..
      2020-08-31  20:09    <DIR>          Desktop
      2020-08-20  16:16    <DIR>          Documents
      2020-06-27  18:21    <DIR>          Favorites
      2020-08-31  22:40             3,005 lorem-ipsum.txt
      2020-06-27  18:21    <DIR>          Music
      2020-06-27  18:21    <DIR>          Pictures
      2020-06-27  18:21    <DIR>          Searches
      2020-06-27  18:21    <DIR>          Videos
                     1 File(s)          3,005 bytes
                     9 Dir(s)  272,352,854,016 bytes free
      

      Change directory
      Առանց արգումենտի cd հրամանը Windows ՕՀ֊ում տպում է աշխատանքային հասցեն (working directory)։

      cd          # change directory to user's HOME on UNIX-like systems
      cd <name>   # change to <name> directory (if exists)
      cd ..       # go up to one directory
      cd ../..    # go up to two directories (\ - backslash on Windows)
      

      Make directory

      mkdir <name>                # creates a new directory <name> in current directory
      mkdir -p <name1>/<name2>    # UNIX-like only - creates chain of directories
      
    • Escape character

      Հատուկ տառանշան (character), որն այլ կերպ է մեկնաբանում հաջորդող տառանշանները։

      Օրինակ՝ տերմինալում բացատ (space) պարունակող հասցեներ ներմուծելու դեպքում տերմինալը մեկնաբանում է բացատը որպես հրամանի կամ արգումենտի ավարտ։ Նման դեպքում կարող ենք տերմինալի «ուշադրությունը շեղենք» բացատից escape characte -ով։ Հասցեների պարագայում, երբ նշվում է, օրինակ, ֆայլի անվանումը, կարելի է անվանումը վերցնել կրկնակի " չակերտի մեջ, օրինակ՝ cd "New Folder":

      UNIX-like ՕՀ֊երի տերմինալում և, առհասարակ, տեքստային ինֆորմացիայի պարագայում հատուկ դեր ունի \ (հակառակ կոտորակ) նշանը:

      DOS/Windows ՕՀ֊երի տերմինալում հատուկ նշան է ^, որը մեկնաբանում է &, |, (, ), <, >, ^ նշանները, իսկ բացատի պարագայում ընդհանուր հատվածը վերցվում է կրկնակի չակերտների " մեջ։ Ծրագրավորման մաս կազմող տեքստային ինֆորմացիայի պարագայում գործում է UNIX-like ՕՀ֊երի նշանային համակարգը։

      # UNIX-like
      mkdir New Folder               # ERROR - too many arguments
      mkdir New\ Folder
      mkdir "New Folder"
      mkdir Mike\'s\ folder          # Mike's folder
      mkdir \"BIG\"\ folder          # "BIG" folder (DOS/Windows doesn't support <">!)
      
      # Windows
      mkdir New Folder               # creates TWO folders - New and Folder
      mkdir "New Folder"
      cd New Folder                  # change directory to New Folder
      cd "New Folder"                # same as above
      echo <tag>                     # ERROR - The syntax of the command is incorrect.
      echo ^<tag^>                   # <tag>
      
    • Control character

      Կարգավորիչ նշան տառանշանների ցանկում (character set), որը չունի տպագրային էլեմենտ ֊ ASCII աղյուսակում՝ 32-ից ցածր բոլոր նշանները, օրինակ՝ 0x0A LF (line feed, newline), 0x0D CR (carriage return) կամ 0x09 TAB։

      Ծրագրավորման մաս կազմող տեքստային ինֆորմացիայի պարագայում, որպես բացառություն, որոշ կարգավորիչ նշաններ escape character \ օգնությամբ ստանում են տառանշանային փոխարինող, օրինակ՝

      \' - single quote
      \" - double quote
      \\ - backslash
      \n - new line
      \r - carriage return
      \t - tab
      \0 - null character (U+0000 NULL) (only if the next character is not a decimal digit)
      \xFF - character represented by the hexadecimal byte "FF"


3.2 Պարադիգմ և կատարման փուլ

[2020-09-02]

  • Տիպային համակարգ

    Ծրագրավորման որոշակի կանոնների ամբողջություն, որոնք «տիպային» հատկությամբ են բնորոշում համակարգչային հավելվածների տարբեր բաղադրիչներ, ինչպիսիք են փոփոխականները (variables), արտահայտությունները (expressions), ֆունկցիաները (functions), և այլ։

    Տիպիզացիայի հիմնական նպատակն է վերհանել տվյալների համադրությամբ պայմանավորված խնդիրները (bug-երը)՝ փոխկապակցելով հավելվածների տարբեր մասերը և նախօրոք ստուգելով դրանց «կայունությունը» (consistency)։ Ստուգումները կարող են կատարվել կազմման (compile-time) կամ կատարման (runtime) փուլերում՝ կախված լեզվի և կատարման միջավայրի տիպիզացիոն առանձնաhատկություններից։

    Typing Example Notes
    Static int x = 2; ՈՒղղակի հայտարարություն
    Dynamic x = 2 Անուղղակի հայտարարություն
    Strong 2 + 1.5 throws type error Տվյալը կցված է որոշակի տիպի
    Week 2 + 1.5 = 3.5 converts 2 to float Տվյալն ազատ է (կարող է փոխարկվել)
    Manifest 2 :int + 1.5 :float throws type error ՈՒղղակի նույնականացում
    Inferred 2 + 1.5 generalize both (:num) types to :fractional Առավել ընդհանրացված տիպի դուրսբերում
    Strict1 new A() not equal to new B() Տվյալները միայն հայտարարված անվանմամբ են նույնատիպ3
    Duck2 new A() equals new B() Տվյալները կարող են համարվել նույնատիպ կառուցվածքով3
    1. Also called Nominal or Nominative
    2. Also called Structural
    3. Data declared as record A {float x; float y;} and record B {float x; float y;}
  • Stack & Heap

    Օպերացիոն համակարգի միջավայրում հավելվածին հասանելի հիշողության կառավարումը (memory management) ենթադրում է որոշակի հիշողության հատկացում և ազատում, երբ դրա կարիքն այլևս չկա։

    Հիշողության ստատիկ հատկացման համար գործարկվում է stack, դինամիկ հատկացման համար՝ heap:

    • Stack

      Այստեղ, յուրաքանչյուր միջակայքի (scope) կատարման ենթակա տվյալները (փոփոխականները) պահվում են հաջորդական տրամաբանությամբ, իսկ ամբողջ հիշողությունը հատկացվում է նախքան կատարման փուլի մեկնարկը։

      Կատարման փուլում առաջնային ֆունկցիան կարող է կանչել (call) մեկ այլ ֆունկցիա՝ դադարեցնելով սեփական կատարումը և սպասելով արդյունքի, հերթական կատարման միջակայքում կարող է կանչվել մեկ ուրիշ ֆունկցիա, և այլն։ Տվյալ հերթականությունը պահպանվում է LIFO (Last In, First Out) տրամաբանությամբ, և պարզեցնում է հիշողության ազատումը յուրաքանչյուր կատարման փուլի ավարտով։

    • Heap

      Անկախ կատարման միջակայքից և հաջորդականությունից՝ տվյալների համար հիշողություն հատկացվում է կատարման փուլում, իսկ հասանելի հիշողությունը սահմանափակվում է միայն ֆիզիկական հիշողությամբ։

      Տվյալ հատվածում տվյալներից օգտվելը համեմատաբար ավելի դանդաղ է տեղի ունենում, սակայն տեղակայված տվյալները հասանելի են բոլոր միջակայքերի և կատարման հոսքերի (threads) համար, իսկ հիշողությունը կարող է հատկացվել և ազատվել ցանկացած պահի։

      Հիշողության ազատումը կարող է կատարվել ուղղակիորեն և ինքնաշխատ: Առաջին դեպքում, չազատված հիշողության պատճառով հնարավոր է «հիշողության արտահոսք» (memory leak)։ Ինքնաշխատ ազատման համար պատասխանատու են «աղբահավաք» հավելվածները (GC - Garbage Collection)։

  • Տվյալների փոխանցում

    Տվյալները կարող են փոխանցվել երկու ձևով՝ արժեքով (by value) կամ հղմամբ (by reference)։ Հղման դեպքում փոխանցվում է տվյալի հատուկ համարը կամ տեղակայման հասցեն:

    Փոխանցման ձևը, կախված ծրագրավորման լեզվից, կարող է լինել ուղղակի (շարահյուսական տարբերակմամբ) և անուղղակի: «Բարձր մակարդակի» ծրագրավորման լեզուների դեպքում, սովորաբար, փոխանցումները տեղի են ունենում առանց շարահյուսական տարբերակման՝ անուղղակի:

    • By Reference

      Տվյալն անուղղակիորեն և հղմամբ այլ փոփոխականի փոխանցելուց հետո կարող է ենթարկվել փոփոխության վերջինի կողմից, եթե տվյալի տիպը փոփոխման ենթակա է (mutable data), և, այդպիսով, կորցնել որոշակիությունը, քանի որ առաջին փոփոխականը ժամանակի տարբեր կետերում կունենա տարբեր արժեքներ։

      Տվյալը ուղղակիորեն (մտածված) և հղմամբ փոխանցելը, օրինակ՝ պահպանման ենթակա տվյալների (DB - Database) պարագայում, կարող է տալ մի շարք առավելություններ։

      // EXPLICIT pass by REFERENCE
      
      #include <stdio.h>
      
      int main()
      {
          int x = 2;
          printf("Address of x: %p\n", &x);
      
          increment(&x);      // pass argument by ADDRESS
          printf("x = %d\n", x);
      }
      
      void increment(int *n)  // get argument by ADDRESS
      {
          printf("Address of n: %p\n", n);
          *n += 1;
          printf("n = %d\n", *n);
      }
      
      Address of x: 0x7ffdd34ce384
      Address of n: 0x7ffdd34ce384
      n = 3
      x = 3
      
      # IMPLICIT pass by REFERENCE
      
      x = [0, 1, 2, 3]
      y = x              # pass by REFERENCE
      
      print("Identifier of x:", id(x))
      print("Identifier of y:", id(y))
      
      x += [4, 5, 6]     # lists are MUTABLE in Python
      
      print("x =", x)
      print("y =", y)
      
      Identifier of x: 140144711080448
      Identifier of y: 140144711080448
      x = [0, 1, 2, 3, 4, 5, 6]
      y = [0, 1, 2, 3, 4, 5, 6]
      
    • By Value

      Տվյալը արժեքով փոխանցելուց, կամ փոփոխման ոչ ենթակա (immutable data) տվյալը հղմամբ փոխանցելուց, նախ, կրկնօրինակվում են նոր հասցեում, այնուհետև փոխանցվում են նոր հասցեի տեսքով։

      // EXCPLICIT pass by VALUE
      
      #include <stdio.h>
      
      int main()
      {
          int x = 2;
          printf("Address of x: %p\n", &x);
      
          increment(x);  // pass argument by VALUE
          printf("x = %d\n", x);
      }
      
      void increment(int n)  // get argument by VALUE
      {
          printf("Address of n: %p\n", &n);
          n++;
          printf("n = %d\n", n);
      }
      
      Address of x: 0x7fff2e58d014
      Address of n: 0x7fff2e58cffc
      n = 3
      x = 2
      
      # IMPLICIT pass by VALUE
      
      x = 2
      print("Identifier of x:", id(x))
      
      def increment(n):  # get argument by REFERENCE
          print("Identifier of n:", id(n))
          n += 1
          print("n =", n)
      
      increment(x)       # pass argument by REFERENCE
      print("x =", x)    # but integers are IMMUTABLE in Python
      
      
      Identifier of x: 140518751926432
      Identifier of n: 140518751926432
      n = 3
      x = 2
      
  • Միևնույն հավելվածի համեմատությունը տարբեր լեզուներով

    Հավելվածի բնութագիրը․

    1. Տպում է թիվ մուտքագրելու հրավեր (ենթադրվում է դրական ամբողջ թիվ, իսկությունը չի ստուգվում),
    2. Ընդունում է մուտքագրված թիվը,
    3. Տպում է զանգված՝ 1-ից մինչև մուտքագրված թիվը,
    4. Տպում է զանգվածի բոլոր թվերի գումարը։

    Օրինակները՝ GitHub։

    Տվյալ օրինակները նախնական ծանոթություն են՝ հաջորդող թեմատիկ դասընթացների առանձին լեզվական առանձնահատկությունները որպես «կանոն» չընդունելու համար։


3.3 Ֆունկցիոնալ ծրագրավորման մոդել

[2020-09-04]

Ծրագրավորման ոլորտում (կիրառական տեսանկյունից) ֆունկցիան ունի մեկ նպատակ՝ մեկ միավորի մեջ «փաթեթավորել» կատարման որոշակի տրամաբանություն։ Դասական function ձևակերպումից բացի հանդիպում են նաև այլ անվանումներ՝ methode, procedure, etc։

Ֆունկցիայի միջակայքում կատարման ենթակա հատվածը անվանում են «ֆունկցիայի մարմին»։ Ֆունցիան կարող է ընդունել մեկ և ավելի արգումենտներ` հղմամբ կամ արժեքով։ Արգումենտների հայտարարությունը ֆունկցիայի հայտարարման անբաժանելի մաս է կազմում, և կախված է լեզվական առանձնաhատկություններից։

Ֆունկցիան կարող է ուղղակի կամ անուղղակի կերպով վերադարձնել իր կատարման արդյունքը՝ նույնպես հղմամբ կամ արժեքով:

function f() {                // no arguments
    console.log("Hello!")     // body
}

function add(x, y) {          // two arguments without types
    return x + y              // body with explicit return
}
(defn add                     ;; declaration with name
  [x y]                       ;; two arguments
  (let [res (+ x y)]          ;; first line of function body
    (res)))                   ;; implicit return of last statement
  • Անանուն ֆունկցիա

    Anonymous function, lambda abstraction - ֆունկցիա որը չունի ցուցիչ (անվանում, իդենտիֆիկատոր)։ Օրինակ հետևյալ դասական ձևակերպումը՝ λx.x, որտեղ λ նշանը հայտարարում է անանուն ֆունկցիա, իսկ . հատուկ նշանը բաժանում է սահմանումը երկու մասերի՝ արգումենտների (ձախ) և ֆունկցիայի «մարմնի» (աջ), որը անուղղակիորեն նաև ֆունկցիայի արդյունքն է։

    Ի տարբերություն հատուկ անվանմամբ ֆունկցիաների (օրինակ՝ f(x) կամ sin(a))՝ անանուն ֆունկցիաներին հղում անել հնարավոր չէ` բացառությամբ այն դեպքերի, երբ անանուն ֆունկցիան հնարավոր է կցել փոփոխականի (ինչը շատ դեպքերում համարժեք է անվանական հայտարարման)։

    Հետևյալ օրինակում անանուն ֆունկցիան ըստ պահանջի միաժամանակ հայտարարվում և փոխանցվում է որպես արգումենտ ֊ GitHub։

  • «Զուտ» ֆունկցիա և «կողմնակի էֆեկտ» (Pure functions and Side-effect)

    Զուտ կամ մաքուր ֆունկցիան պարտավոր է բավարարել հետևյալ պայմաներին․

    1. նույն արգումենտների փոխանցման պարագայում միշտ վերադարձնել նույն արդյունքը (առանց միջակայքից դուրս հասանելի տվյալների հետ հարաբերվելու),
    2. կատարվել առանց «կողմնակի էֆեկտ» (առանց միջակայքից դուրս հասանելի տվյալների փոփոխության և IO (input-output) ազդեցության)։

    Նախորդ օրինակում f() ֆունկցիան ոչինչ չի վերադարձնում, սակայն կատարումն ունի կողմնակի էֆեկտ՝ IO ֆունկցիայի կանչ (տեքստային արտաբերում), ի տարբերություն add(x, y) «զուտ »ֆունկցիայի, որը ոչ մի կերպ չի հարաբերվում «արտաքին աշխարհի» հետ։

    Այլ օրինակներ՝ GitHub։

  • Առաջին կարգի ֆունկցիա

    First-class function - այն ֆունկցիաներն են, որոնք դասվում են որպես «առաջին կարգի օբյեկտ», և, հետևաբար, կարող են մաս կազմել այլ օբյեկտների նկատմամբ կիրառվող բոլոր գործողությունների։ Օրինակ՝ կցվել փոփոխականների, փոխանցվել ֆունկցիայի որպես արգումենտ, վերադարձվել որպես ֆունկցիայի արդյունք, և այլն։ Առաջին կարգի ֆունկցիաներ են, ըստ էության, անանուն ֆունկցիաները։

  • Ֆունկցիա֊տվյալ վերացականություն

    Մաթեմատիկական տեսանկյունից Ֆունկցիան դիտարկվում է որպես հարաբերություն որոշակի մուտքային և ելքային միավորների միջև։

    In mathematics, a function is a binary relation over two sets that associates every element of the first set, to exactly one element of the second set."

    Wikipedia

    Այսպիսով, մասնավորապես «զուտ» ֆունկցիաների պարագայում, կարելի է պնդել, օրինակ, որ f ≡ f', եթե f(x) = x², f' = {... (2: 4), (3: 9), (4: 16) ...} (զանգվածը տեսականորեն պարունակում է հնարավոր բոլոր մուտքային և ելքային արժեքների համադրությունները)։

    Անանուն «զուտ» ֆունկցիաների դեպքում զուգահեռները կարող են ավելի ակնհայտ դառնալ՝

    var f  = () => 42
    var f' = 42
    
  • Հղման թափանցիկություն (Referential Transparency)

    Ֆունկցիան կարող է համարվել «հղմամբ թափանցիկ», եթե այն կարող է փոխարինվել համապատասխան արժեքով՝ առանց ծրագրի վարքագծի փոփոխության։

    Տվյալ ֆունկցիաները, ըստ էության, կարելի է դիտարկել որպես «զտվածության» (purity) և անփոփոխ տվյալների (immutable data) համադրություն, օրինակ՝ երբ ֆունկցիան հարաբերվում է միջակայքից դուրս հայտարարված և ընդհանուր հասանելիության անփոփոխ տվյալների հետ։

    Օրինակ՝ GitHub։


3.4 Օբյեկտ֊օրիենտացված մոտեցում (Java)

[2020-09-07]

Դասընթացներին վերաբերվող ծրագրային օրինակները հիմնականում վերաբերում են դասընթացի վերնագրում նշված լեզվին՝ բացառությամբ հատուկ նշված ընդհանուր կամ մասնավոր դեպքերի։

Որպես «ծայրահեղ» օբյեկտ֊օրիենտացված՝ Java լեզվում ցանկացած կոդ պետք է լինի որևէ կլասի միջակայքում, նույնիսկ եթե չի ստեղծվում ոչ մի օբյեկտ։ Կլասը, որպես կանոն, ստեղծվում է համանուն ֆայլում և հայտարարվում է ամենավերին մակարդակում։ Կլասերի քանակը մեկ ֆայլում սովորաբար սահմանափակված չէ, սակայն ընդունված է պահպանել մեկ ֆայլում մեկ կլաս ունենալու սկզբունքը։

  • Class

    Կլաս են կոչվում այն «ձևանմուշները» (template), որոնց օգնությամբ օբյեկտ֊օրիենտացված ծրագրավորման մեջ ստեղծվում են հիմնարար տարրերը՝ Object (օբյեկտ)։ Այստեղ հայտարարվում են Կլասին վերաբերվող բոլոր բաղադրիչները՝ Fields (տվյալներ), Constructors (օբյեկտի կոնստրուկտորներ), Methods (մեթոդներ), Nested Classes (ներդրված կլասեր)։ Կլասի տվյալներին տարբեր լեզուներում անվանում են՝ Field (Java), Property, Member, Attribute, և այլն։

    public class Person {
    
        // Fields
        String name;
        int age;
    
        // Constructor
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        // Method returning String representation of object
        public String toString() {
            return "Name: " + this.name + ", Age: " + this.age;
        }
    }
    
    • Objects (instances)

      Կլասի հիմքում ստեղծվում են համապատասխան տվյալներ և «գործողություններ» (methods) կրող օրինակները (instances)` օբյեկտները։ Օբյեկտները ստեղծվում են Heap-ում new օպերատորի օգնությամբ, որն, իր հերթին, մեկնարկի է բերում տվյալ կլասի կոնստրուկտորը` փոխանցելով անհրաժեշտ տվյալները։ Եթե կոնստրուկտոր ուղղակիորեն հայտարարված չէ՝ անուղղակիորեն գործում է առանց արգումենտների լռելյայն (default) կոնստրուկտորը։

      Ստեղծվող օբյեկտները, որպես կանոն, հղմամբ կցվում են համապատասխան փոփոխականների՝ բացառությամբ այն դեպքերի, երբ հղումը պահպանման կարիք չունի և օբյեկտը փոխանցվում է անմիջապես հայտարարմամբ։

      // passing constructor arguments
      Person objJohn = new Person("John", 21);
      Person objPaul = new Person("Paul", 42);
      
      System.out.println(objJohn);
      System.out.println(objPaul);
      
      System.out.println(new Person("Peter", 84));  // inline init
      
      Name: John, Age: 21
      Name: Paul, Age: 42
      Name: Peter, Age: 84
      
    • Fields (տվյալներ)

      Տվյալները կարող են կազմված լինել պրիմիտիվներից և օբյեկտներից։ Այսինքն, կլասը կարող է հայտարարվել տվյալներով, որոնք պատկանում են սեփական (ռեկուրսիվ ներդրվածություն) կամ մեկ այլ կլասի։

    • Methods

      Մեթոդները կլասի միջակայքում հայտարարված ֆունկցիաներ են, որոնք հասանելի են կլասի (static methods) կամ օբյեկտի միջոցով: Օրինակ՝ վերոնշյալ օրինակում john փոփոխականին կցվող օբյեկտի միակ toString մեթոդը կատարման համար հասանելի է john.toString() արտահայտությամբ, որի արդյունքը String տիպի օբյեկտ է։

    • Scope & Access modifiers

      Հետևյալ աղյուսակում նշված են կլասերի, կոնստրուկտորների, մեթոդների և տվյալների հասանելիության կարգերը։

        Class Package Subclass (same pkg) Subclass (diff pkg) World
      public * * * * *
      protected * * * *  
      no modifier * * *    
      private *        
  • Data
    • Primitives

      Պրիմիտիվ տվյալներին են պատկանում տվյալների մեքենայական մակարդակի տիպերը, որոնցից են.

      • int 32-bit կրողունակությամբ ամբողջ թվերի տիպը` իր ենթատիպերով (byte 8-bit, short 16-bit, long 64-bit),
      • float և double, համապատասխանաբար եզակի և կրկնակի ճշգրտության, տասնորդական (լողացող կետով) թվերը,
      • char տառանշային տիպը՝ unicode կոդավորմամբ,
      • boolean տրամաբանական true (ճշմարիտ) և false (սխալ)։
    • Objects (type)

      Օբյեկտային տիպը, մի դեպքում, կարող է հանդես գալ որպես պրիմիտիվ տվյալի «տիպ֊շապիկ» (wrapper-type), օրինակ՝ Integer, Boolean, Character: Մյուս դեպքում, առանձին կլասի օրինակ (instance), օրինակ` Person: Օբյեկտի հղում կրող փոփոխականների տիպը հայտարարվում է սեփական կամ «ծնողական» (ժառանգականություն) կլասի անունով։

    • Null

      Կատարման տարբեր փուլերում օբյեկտային տիպերը կարող են հայտարարվել և փոխանցվել null հղմամբ՝ ուղղակի կամ անուղղակի կերպով։

      Person objJohn = null;                 // explicit init
      objJohn.toString();                    // NullPointerException
      
      
      // list() on File object returns null if 'directory' does not exist
      String[] listOfDirs = new File("directory").list();
      
      // listOfDirs is null
      String firstDir = listOfDirs[0];    // NullPointerException
      

      Առավել հաճախ հանդիպող խնդիր է null հղմանը որևէ մեթոդ կամ հրաման հաջորդաբար փոխանցելը, ինչը բերում է ամբողջ ծրագրի կատարման խափանմանը։

    • Comparison

      Տվյալների համեմատությունը կախված է տվյալների տեսակից։

      • Պրիմիտիվ տվյալները համեմատվում են == (հավասարություն) կամ != (անհավասարություն) օպերատորների օգնությամբ՝ ըստ արժեքի։
      • Օբյեկտները համեմատվում են == կամ != օպերատորների օգնությամբ՝ ըստ հղման, և կլասում ուղղակիորեն հայտարարված equals() մեթոդի օգնությամբ, եթե երկու օբյեկտները պատկանում են նույն կլասին։
      int x = 42;
      x == 42;                   // true
      objJohn == objPaul;        // false
      
      Person objJohn2 = new Person("John", 21);
      objJohn == objJohn2;       // false (different references)
      objJohn == objJohn;        // true (same reference, same object)
      objJohn.equals(objJohn2);  // may be true depending on 'equals' implementation
      
  • Control Statements
    • Selective

      Պայմանային արտահայտությունները կարող են լինել ներդրված։

      //IF-THEN-ELSE
      if (x > 0)
          // ... one-line satement without enclosing { }
      else {  // Case that doesn't match previous
          // ... milti-line code
          // ... with enclosing { }
      }
      
      
      //IF-ELSE-IF-THEN-ELSE
      if (x > 0)
          // ...
      else if (x == 0)
          // ... 
      else {  // Case that doesn't match all previous
          // ...
      }
      
      
      // SWITCH
      int numDays;
      switch (monthNum) {
          case 1: case 3: case 5:
          case 7: case 8: case 10:
          case 12:
              numDays = 31;
              break;
          case 4: case 6:
          case 9: case 11:
              numDays = 30;
              break;
          case 2:
              if (isLeapYear)
                  numDays = 29;
              else
                  numDays = 28;
              break;
          default:
              System.out.println("Invalid month.");
              break;  // optional if the last
      }
      
      
      // TERNARY (ELVIS) OPERATOR - ?:
      String john = (objJohn != null) ? objJohn.toString() : "objJohn is null"
      
    • Repetitive

      Ցիկլիկ արտահայտությունները նույնպես կարող են լինել ներդրված։

      // WHILE LOOP
      int n = 0;
      while (n < 10) {   // check on every iteration
      
          if (caseWeAreSearching) {
              continue;  // jump to next iteration
          }
      
          // ... do some stuff
      
          n++;   // increment n
      }
      
      
      // DO-WHILE LOOP
      do {
          // ... do some stuff (at least once)
          n++;   // increment n
      } while (n < 10);  // check after first iteration
      
      
      // array example for iterating over
      int[] arr = {1,2,3,4,5,6,7,8};
      
      
      // CLASSIC LOOP ITERATING BY INDEX
      for (int i = 0; i < arr.length; i++) {
          int elem arr[i];
          // ... do some stuff with element
      }
      
      
      // FOR-EACH LOOP ITERATING BY ELEMENT
      for (int elem : arr) {
      
          // check some conditions
          if (elem == whatWeAreSearching) {
              // ... do some stuff
              break;  // exit current loop
          }
      }
      

3.5 Ինկապսուլյացիա, ժառանգականություն, պոլիմորֆիզմ (Java)

[2020-09-09]

  • Encapsulation

    Ինկապսուլյացիայի նպատակը ծրագրի տարբեր հատվածներ տրամաբանորեն և/կամ հասանելությամբ առանձնացնելն է, որը թույլ է տալիս թաքցնել տվյալներ ու ծրագրային իրականացման մանրամասները՝ միմյանց ուղղակիորեն տրամադրելով միայն փոխհամագործակցության համար անհրաժեշտ հատվածները։

    • Package and import

      Կլասերը ըստ տրամաբանության խմբավորելու միավորը կոչվում է «փաթեթ» (package)։ Փաթեթների որոշակի ներդրվածությունը իրենից ներկայացնում է փոխադարձ հասցեավորման համար անհրաժեշտ նույնականացման մոդել։ Ինչպես էլ կլասի անվան դեպքում՝ փաթեթների անվանումները կրկնում են ֆայլային համակարգի համապատասխան հասցեն, oրինակ՝ ./data/model/Model.java ֆայլը պետք է առաջին տողում հայտարարի իր փաթեթը package data.model;, իսկ տվյալ ֆայլի համանուն կլասը այլ ֆայլում ներմուծելու համար պետք է օգտագործել import data.model.Model; կամ import data.model.*; և ներմուծել բոլոր ֆայլերի բոլոր կլասերը։

      Կազմակերպությունների համար գոյություն ունի «փաթեթավորման» համաձայնություն՝ օգտագործել սեփական վեբ֊հասցեի հակառակ հաջորդականությունը, օրինակ՝ com.sun.eng կամ org.apache.commons:

      Java API֊ը տրամադրում է բազմաթիվ փաթեթներ, որոնցից են՝ java.lang, java.utils, java.io և այլ (Java SE 14 & JDK 14

      java.lang փաթեթի բովանդակությունն անուղղակի կերպով միշտ ներառվում է և ներմուծման կարիք չունի։

    • Class and fields

      Տվյալների իկապսուլյացիայի հիմնական գործիքը հասանելիության կարգերն են, որոնց միջոցով հայտարարվում են կլասերի, կոնստրուկտորների, մեթոդների և տվյալների հասանելիության կարգը տարբեր մակարդակներում։

      Կլասի օրինակներին վերաբերվող տվյալներն, ըստ ընդունված կարգի, հայտարարվում են private, այսինքն՝ կլասի միջակայքից դուրս անհասանելի, իսկ հասանելիությունն ապահովում են համապատասխան Getter և Setter մեթոդները՝ սահմանափակելով տվյալների ուղղակի փոփոխումը։

      Հետևյալ օրինակում absoluteValue փոփոխականն անհասանելի է, հակառակ դեպքում փոփոխականին հնարավոր կլիներ ուղղակիորեն կցել բացասական թիվ, ինչը հայտարարված տրամաբանությունից դուրս է։

      // no modifier
      // available for package member and subclasses
      class AbsoluteValue {
      
          // private field
          private int absoluteValue;
      
          // Constructor uses setter
          public AbsoluteValue(int value) {
              absolutevalue = setValue(value);
          }
      
          // Getter
          public getAbsoluteValue() {
            return absolutevalue;
          }
      
          // Setter
          public void setAbsoluteValue(int value) {
      
              // check input
              if (checkValue(value)
                  absoluteValue = value;
              else
                  absolutevalue = Math.abs(value);
          }
      
          // private methode
          private boolean checkValue(int value) {
              return value >= 0;
          }
      } 
      

      Ներքին տրմաբանության մեթոդները նույնպես կարող են լինել հասանելությամբ սահմանափակված։

    • Nested classes

      Ներդրված կլասերը նույնպես օգտագործվում են ինկապսուլյացիայի նպատակով, երբ օբյեկտային որևէ մոդել ըստ իր տրամաբանության հանդիսանում է «ներքին» օգտագործման։

      class OuterClass{
      
        // ... class block
      
        private void innerMethod() {
            InnerClass innerObj = new InnerClass();
            // ... doing some stuff with inner object
        }
      
        private InnerClass {
            // ... encapsulated structure
        }
      }
      
    • Anonymous classes

      Նախորդ օրինակում կլասը կարող էր ներդրված լինել նաև մեթոդի ներսում, քանի որ չէր օգտագործվում innerMethod մեթոդից դուրս։ Այդպիսի դեպքերում կլասը կարելի է հայտարարել անանուն, եթե պահանջվում է այդ կլասի միայն մեկ օրինակ։

      Անանուն կլասը հայտարարվում է նոր օբյեկտի ստեղծման օրինակով՝ ավելացնելով կլասի հայտարարման «մարմինը» { } փակագծերի մեջ։ Կլասի օրինակը, օբյեկտը, տվյալ դեպքում ստեղծվում է չհայտարարված լռելյայն (default) կոնստրուկտորի օգնությամբ։

      class OuterClass{
      
          // ... class block
      
          private void innerMethod() {
      
              // creates an inplace instance
              InnerAnonymous innerObj = new InnerAnonymous() {
                  // ... anonymous class blocks
              };
          }
      }
      

      Անանուն կլասը կարելի է հայտարարել նաև՝ փոխանցելով այն որպես արգումենտ։

      // sampleMethod takes InnerClass object
      sampleObject.sampleMethode( new AnonymousClass() {
          // ... anonymous class block
      });
      
  • Inheritance

    Ժառանգականությունը օբյեկտային ծրագրավորման հենասյուներից է, որը թույլ է տալիս ժառանգել և վերաօգտագործել (reuse) մեկ այլ կլասի դաշտերը և մեթոդները։

    Oրինակ, եթե հայտարարենք հետևյալ կլասը, որն արտահայտում է երկրաչափական ուղղանկյան տրամաբանություն՝

    // parent class example
    class Rectangle {
    
        double width;
        double height;
    
        // rectangle constructor
        public Rectangle(double width, double height) {
            this.width = width;
            this.height = height;
        }
    
        public double area() {
            return width * height;
        }
    }
    

    Square (քառակուսի) կլասի համար այն կարող է ծառայել որպես ծնողական կլաս, իսկ ժառանգող կլասը այլևս կարիք չի ունենա հայտարարել կրկնող տրամաբանությամբ դաշտեր և մեթոդներ (այնուամենայնիվ, ծնողական կլասի private դաշտերը և մեթոդները ժառանգող կլասում հասանելի չեն, հասանելության համար հարկավոր է ծնողական կլասում դրանք հայտարարել protected

    // child class with constructor only
    class Square extends Rectangle {
    
        // square constructor
        public Square(double side) {
            // calling parent's constructor
            super(side, side);  // as width and height
        }
    }
    
    Square square = new Square(5);
    
    // calling inherited method
    double squareArea = square.area();  // => 5
    

    Ժառանգող կլասը կարող է հայտարարել իր սեփական դաշտերն ու մեթոդները, ինչպես նաև՝ վերահայտարարելով «ծածկել» (override) ծնողական մեթոդները։

    // child class
    class Square extends Rectangle {
    
        // ... class block
    
        // Override parent's method (override)
        public double area() {
    
            // ... do some stuff
    
            // calling parent's area method for result
            return super.area();
        }
    }
    

    Ժառանգականության աստիճանը սահմանափակված չէ, սակայն ժառանգել միաժամանակ կարելի է միայն մեկ կլասից:

    • Abstract classes

      Աբստրակտ (վերացական) կլասերը կարող են փոխարինել ծնողական կլասերին այն դեպքերում, երբ ծնողական կլասը տրամաբանորեն վերացական է, և վերջինիս օբյեկտի (օրինակի) ստեղծում չի ենթադրվում։

      Ի տարբերություն սովորական կլասերի՝ աբստրակտ կլասի մեթոդները կարող են նույնպես հայտարարվել աբստրակտ։ Այդ դեպքում մեթոդը բովանդակություն չի ունենում, սակայն տվյալ աբստրակտ կլասը անմիջականորեն ժառանգող բոլոր կլասերը պարտավոր են ռեալիզացնել ծնողական աբստրակտ մեթոդը։

      public abstract class GeometryShape {
      
          // fields
          // non-abstract methods
      
          abstract double area();  // empty
      }
      
      
      class Triangle extends GeometryShape {
      
          // mandatory declaration
          public double area() {
            // calculating area for Triangle
          }
      }
      
    • Interfaces

      Ինտերֆեյսը նման է աբստրակտ կլասի։ Այն չի կարող ունենալ անմիջական օբյեկտ (օրինակ), և կարող է պարունակել բովանդակությամբ և առանց բովանդակություն մեթոդներ։ Սակայն, ի տարբերություն աբստրակտ կլասի, ինտերֆեյսի բոլոր մեթոդները ունեն լռելյայն (by default) public հասանելություն։

      Ինտերֆեյսների մեկ այլ առանձնահատկությունն այն է, որ կլասերը կարող են «իրականացնել» (implement) մեկից ավելի ինտերֆեյս։ Բացի դրանից, ինտերֆեյսը ժառանգական տրամաբանությունից անկախ է և կարող է իրականացման «օրինակ» ծառայել տրամաբանորեն տարբեր կլասերի համար։ Ինտերֆեյսը այլ կերպ անվանում են նաև «կոնտրակտ», քանի որ ինտերֆեյսը երաշխավորում է մեթոդի առկայությունը։

      public interface Measurable {
      
          // public by default
          double getValue();
      }
      
      
      class Angle implements Measurable, BiComponent {
          // implementing getValue()
          // implementing all methods of BiComponent
      }
      

      Ինտերֆեյսը կարող է ժառանգել (extends) մեկ այլ ինտերֆեյս։

  • Polymorphism

    Պոլիմորֆիզմ են անվանում այն հատկությունը, երբ տվյալի տիպիզացված միավորը ներկայանում է այլ կերպ։ Օբյեկտները (կլասի օրինակների) պոլիմորֆ են ըստ էության, և կարող են ներկայանալ որպես ցանկացած ծնողական կլաս կամ իրականացվելիք ինտերֆեյս:

    Հետևյալ օրինակում Android կլասը ժառանգող GalaxyA51 կլասը վերահայտարարել է միայն check() ծնողական մեթոդը, և հայտարարել է cpu() սեփական մեթոդը։ Քանի որ main մեթոդում ստեղծվող GalaxyA51 կլասի օբյեկտի հղումը կցվում է ծնողական Android տիպին, ապա կազզման (compilation) ընթացքում տեղծվող օբյեկտը ներկայանում է որպես ծնողական կլասի օբյեկտ, և հաշվարկվում են միմիայն հղման կլասի մեթոդները։ Այս դեպքում օբյեկտը, լինելով GalaxyA51 տիպի, չի կարող օգտագործել իր սեփական cpu() մեթոդը, սակայն վերահայտարարված (override) check() մեթոդի դեպքում կգործարկվի հենց GalaxyA51 կլասի մեթոդը, քանի որ այն հաշվարկվել է կազզման ընթացքում և ժառանգականության տրամաբանությամբ «ամենամոտն է»։

    abstract class Android {
    
        // not overridden
        public String kernel() {
            return "has Linux kernel";
        }
    
        // OVERRIDDEN
        public String check() {
            return "is Android";
        }
    }
    
    
    class GalaxyA51 extends Android {
    
        // child-specific
        public String cpu() {
            return "has Exynos CPU";
        }
    
        // OVERRIDES Parent's check()
        public String check() {
            return "is Galaxy A51";
        }
    }
    
    
    class Main {
        public static void main(String[] args) {
    
             // parent reference type
             Android objA51 = new GalaxyA51();
    
             // objA51.cpu() will throw an ERROR
             // cannot find 'cpu' symbol
    
             // overridden check() method invoked (see below)
             System.out.println("objA51 " + objA51.check());
        }
    }
    
    objA51 is Galaxy A51
    

    Օբյեկտ֊օրիենտացված լեզուներում բոլոր կլասերն ունեն լեզվի մաս կազմող ամենաբարձր ծնողական կլաս, որն ուղղակիորեն կամ անուղղակիորեն ժառանգում են, և որին կարող են հղում անել ստեղծվող օբյեկտերի փոփոխականները։ Java լեզվում այն կոչվում է Object, որն աանուղղակիորեն (առանց հայտարարելու) ժառանգում են բոլոր կլասերը։

    Կա օբյեկտի (ոչ փոփոխականի) իրական տիպը ստուգելու երկու տարածված տարբերակ՝ ըստ ժառանգականության և ըստ ուղիղ համեմատության։ Առաջին դեպքում կարելի է պնդել, որ օբյեկտը նշված կամ ժառանգորդ կլասի օրինակ է։ Երկրորդ դեպքում կատարվում է օբյեկտի կլասի ուղղակի համեմատություն։ Երկու դեպքում էլ կարելի է օբյեկտը «ստվերել» (cast) ստուգված կլասով և հայտարարել նոր փոփոխական կամ տեղում փոխանցել որպես արգումենտ։

    // valid assignment
    Object objA51 = new GalaxyA51();
    
    // only Object's methods are available
    objA51.toString();
    
    // checking inheritance
    if (objA51 instanceof Android) {
        // cast by own or parent class
        Android newA51 = (Android) objA51;
    
        // ... do some stuff with new type
    }
    
    // checking exact class
    if (objA51.getClass() == GalaxyA51.class) {
        // cast by own class
        GalaxyA51 newA51 = (GalaxyA51) objA51);
    
        // ... do some stuff with new type
    }
    

3.6 Օբյեկտ֊օրիենտացման գործիքներ և հավաքածուներ (Java)

[2020-09-11]

  • Entrypoint

    C լեզվաընտանիքում ընդունված է օպերացիոն համակարգի միջավայրում կատարման ենթակա ծրագրային հավելվածը մեկնարկել ուղղակի կերպով հայտարարված main ֆունկցիայի օգնությամբ, որը համակարգային ավելի «ցածր մակարդակի» (low-level) կատարման պարագայում, որպես կատարման արդյունք, վերադարձնում է int ամբողջ թիվ՝ փոխանցելով կատարումը նախաձեռնող «ավելի բարձր» պրոցեսին (օրինակ՝ տերմինալին, կամ այլ հավելվածին) կատարման ավարտի կոդը (exit status)։ Ընդունվող արգումենտները պատասխանատու են միջավայրից փոխանցվող արգումենտների համար (տես՝ հաջորդ պարբերություն)։ Առաջին արգումենտը որոշ լեզուների դեպքում կատարման ֆայլի անունն է։

    // argc - arguments' count
    // argv - arguments' strings
    // return int
    int main(int argc, char *argv[]) {
    
        // MAIN PROCEDURES
    }
    

    Վիրտուալ մեքենայում կատարման ենթակա հավելվածների պարագայում main ֆունկցիան հայտարարվում է որպես որևէ մի կլասի (անվանումը կարևոր չէ) ստատիկ մեթոդ։ Վերադարձվող արժեքը բաց է թողնվում (void), քանի որ վիրտուալ մեքենան տիրապետում է կատարման ավարտի մասին ամբողջ ինֆորմացիային, իսկ փոխանցվող արգումենտը՝ միջավայրից փոխանցվող արգումենտների զանգվածն է։

    import java.util.Arrays;
    
    // class name doesn't matter
    class Entrypoint {
    
        public static void main(String[] args) {
    
            if (args.length > 0) {
    
                // print args passed by running process
                System.out.printf("Passed args: %s\nType: %s\n", 
                                  Arrays.toString(args),
                                  args.getClass().getSimpleName());
            }
        }
    }
    

    Տվյալ դեպքում Entrypoint կլասի կատարումը համապատասխան հրամանի օգնությամբ սկսվում է main մեթոդից, որին փոխանցվում են կատարման հրամանի պայմանական արգումենտները՝ argument1 և argument2, և որը, ըստ վերը նշված իրականացման, արտաբերում է փոխանցված արգումենտները։

    # run main of Entrypoint class and pass args
    java Entrypoint argument1 argument2
    
    Passed args: [argument1, argument2]
    Type: String[]
    
  • Static & Final components
    • Static fields and methods

      Ստատիկ կարող են հայտարարվել կլասի բոլոր բաղադրիչները՝ տվյալները, մեթոդները և ներդրված կլասերը։ Ստատիկ բաղադրիչները, ի տարբերություն սովորական բաղադրիչների, պատկանում են ոչ թե օբյեկտին, այլ կլասին, հետևաբար՝ ստատիկ հայտարարված տվյալներն ունեն եզակի օրինակ։

      Այդպես, օրինակ, static հայտարարված դաշտը կարող է կրել կլասի օրինակների քանակի մասին տվյալ` կոնստրուկտորում ավելացում հաշվելով, իսկ տվյալի հասանելիությունը կարող է ապահովել համապատասխան ստատիկ մեթոդը, որն այլ կերպ անվանում են կլասի մեթոդ։ Օբյեկտների (ոչ ստատիկ) մեթոդների համար ստատիկ տվյալները հասանելի են (ինչպես երևում է կոնստրուկտորի դեպքում), իսկ ստատիկ մեթոդները՝ ոչ։

       class Factory {
      
         // static field - unique data
         private static int numberOfObjects = 0;
      
         // non-static fields
      
         public Factory() {
             // ... do some stuff
             numberOfObjects++;  // increment
         }
      
         // class-method (static)
         public static int getNumberOfObjects() {
             return numberOfObjects;
         }
       }
      
      
      class Main {
          public static void main(String[] args) {
      
              // creating objects
              Factory objF = new Factory();
              Factory objS = new Factory();
              Factory objT = new Factory();
      
              // class-method invokation
              int num = Factory.getNumberOfObjects();
      
              System.out.println("Number of objects: " + num);
          }
      }
      
      Number of objects: 3
      

      Ներդրված կլասի static հայտարարումը թույլ է տալիս ապահովել ներքին կլասի հասանելիությունը արտաքին կլասի միջոցով։

      class Outer {
          // outer class stuff
      
          static class Inner {
              // inner class stuff
          }
      }
      
      
      class Main {
          // declaration via outer class
          Outer.Inner innerClassObj = new Outer.Inner();
      }
      
    • Final components

      Փոփոխականները, մեթոդները և կլասերը այս օպերատորի օգնությամբ ստանում են համապատասխան սահմանափակումներ։

      Բաղադրիչ Վարքագիծ
      Փոփոխական Փոփոխականի հղումը չի կարող փոխվել (reference is constant)
      Մեթոդ Մեթոդը չի կարող վերահայտարարվել (prevent override)
      Կլաս Կլասից ժառանգել հնարավոր չէ (prevent inheritance)

      Պրիմիտիվ տվյալների դեպքում փոփոխականը կարելի է ընդունել որպես հաստատուն, քանի որ պրիմիտիվ տվյալները համարվում են «անփոփոխ» (immutable), իսկ հղումը չի կարող փոխվել։

      Օբյեկտներին հղում արվող փոփոխականների դեպքում անփոփոխ է մնում միայն օբյեկտի հղումը, օբյեկտը բովանդակությամբ ենթակա է փոփոխության (mutable)՝ բացառությամբ նախանշված անփոփոխ օբյեկտների (օրինակ՝ String

  • Overloaded constructor & methods

    Մեթոդների վերաբեռնավորումը (overloading) ևս մեկ օրինակ է, որը թույլ է տալիս տարբեր տիպի տվյալների դեպքում օգտագործել միևնույն մեթոդը։ Օրինակ՝ ամբողջական 64-bit long, ամբողջական 32-bit int և տասնորդական double տվյալների դեպքում հաշվարկային մեթոդները կարող են կրկնվել: Այս դեպքում համանուն մեթոդները կարող են վերաբեռնավորվել ըստ արգումենտների տիպի և քանակի։

    Պրիմիտիվ արգումենտներն առանց տիպի ուղղակի նշման փոխանցելու դեպքում մոտարկում է արվում հնարավոր «անկորուստ» հաշվարկային տիպի։ Համընկնում չլինելու պարագայում կատարվում է անուղղակի փոխարկում, որի ընթացքում հնարավոր է ինֆորմացիայի կորուստ, օրինակ՝ long և int միջև։

    class SumClass {
    
        public static int sum(int x1, int x2) {
            return x + y;
        }
    
        public static long sum(long x1, long x2) {
            return x + y;
        }
    
        public static double sum(double x1, double x2) {
            return x + y;
        }
    }
    
    
    class Main {
      // using different methods
      SumClass.sum(42, 3215468);    // 1st
      SumClass.sum(2147483648, 1);  // 2nd - Integer.MAX_VALUE = 2147483647
      SumClass.sum(0.1, 7);         // 3th - double
    
      // ...
    }
    

    Վերաբեռնավորել կարելի է նաև կլասի կոնստրուկտորը, վերաօգտագործելով արդեն առկա հայտարարումը։

    class Rectangle {
    
        double width;
        double height;
    
        // ordinary rectangle constructor
        public Rectangle(double width, double height) {
            this.width = width;
            this.height = height;
        }
    
        // square case constructor
        public Rectangle(double side) {
            // reuse matching appropriate constructor
            this(side, side);
    
            // constructor call must be the first statement
        }
    }
    
    
    class Main {
        // using different constructors
        Rectangle rectangle = new Rectangle(3, 4);
        Rectangle square = new Rectangle(5);
    }
    
  • Parametric polymorphism (Generics)
    • Generic Classes

      Կլասերի վերաօգտագործման պարագայում հաճախ առաջանում է տիպիզացման կախվածության խնդիր։ Օրինակ միևնույն բաղադրիչի երկու օրինակ պարունակող BiComponent կլասը ամեն անգամ պետք է տիպիզացվի ըստ բաղադրիչի տիպի:

      class ButtonBiComponent {
          private Button button1;
          private Button button2;
      
          // ... constructor and methods
      }
      
      class SpeakerBiComponent {
          private Speaker speaker1;
          private Speaker speaker2;
      
          // ... constructor and methods
      }
      
      // ... so on and so forth
      

      Այստեղ օգնության են գալիս այսպես կոչված «ջեներիկները» (Generics), որոնք թույլ են տալիս կլասը պարամետրիզացնել (ընդհանրացնել) փոփոխական տիպով։

      // parameterized class
      class BiComponent<T> {
      
          // parameterized field
          private T component1;
          private T component2;
      
          // parameterized constructor
          public BiComponent(T component1, T component2) {
              this.component1 = component1;
              this.component2 = component2;
          }
      
          // getters and setters
      }
      
      
      class Main {
         // ... some initializations
      
         BiComponent<Button> btnPair = new BiComponent<>(btn1, btn2);
         BiComponent<Speaker> spkrPair = new BiComponent<>(spkr1, spkr2);
      }
      

      Պարամետրիզացիան կարող է լինել մեկից ավելի տիպերի համար։

      // parameterized class
      class Pair<T1, T2> {
      
          // parameterized field
          private T1 component1;
          private T2 component2;
      
          // parameterized constructor
          public Pair(T1 component1, T2 component2) {
              this.component1 = component1;
              this.component2 = component2;
          }
      
          // getters and setters
      }
      
      
      class Main {
         // ... some initializations
      
         Pair<Button, Speaker> btnSpkr = new Pair<>(btn1, spkr2);
      }
      

      Պոլիմորֆ կլասերի տիպիզացիան Java լեզվի դեպքում տեղի է ունենում կազմման փուլում։ C# լեզվում, օրինակ, տիպիզացիան տեղի է ունենում կատարման փուլում, ինչը հավելյալ ճկունության հնարավորություն է տալիս։

    • Generic Methods

      Մեթոդների պարամետրիզացիան օգնում է ընդհանրացնել այն բազմաթիվ կլասերի օբյեկտների համար՝ ըստ փոփոխականների, որոնք առանձին նշվում են < > հատուկ «չակերտների» մեջ։

      class Components {
          // ... some initializations
      
          // return value and argument are parameterized by T
          public static <T> T getFirstComponent(BiComponent<T> bc) {
              return bc.getFirstComponent();
          }
      
          // arguments are parameterized by K and V
          public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
              return p1.getKey().equals(p2.getKey()) &&
                     p1.getValue().equals(p2.getValue());
        }
      }
      
  • Arrays & Collections

    Զանգվածները (Array), C լեզվաընտանիքի ավանդույթի համաձայն, հայտարարվում են [] փակագծերի միջոցով՝ նշելով ծավալը, որը փոփոխման ենթակա չէ։ Ի տարբերություն ավելի ցածր մակարդակի (low-lewel) լեզուների՝ զանգվածները Java լեզվում օբյեկտ են և կարող են պարունակել պրիմիտիվ տվյալներ կամ օբյեկտներ։

    // initializing empty array by length 
    int[] intArray = new int[20];
    
    // initializing array by values
    int[] intArray = {1,2,3,4,5,6,7};
    

    Հավաքածուները (Collections) զանգվածներից տարբերվում են դինամիկ ծավալով և համեմատաբար ավելի ցածր արագագործությամբ։ Հավաքածուները պոլիմորֆ են պարամետրիկ փոփոխականով՝ ըստ պարունակության, կարող են լինել միատարր և բազմատարր։

    Հավաքածուները կարող են հայտարարվել դատարկ, որից հետո կարող են ընդունել և հեռացնել էլեմենտներ։ Ի տարբերություն զանգվածների՝ կարող են պարունակել միայն օբյեկտներ։ Պրիմիտիվ տվյալների համար կան «փաթեթավորող» (wrapper) կլասեր, որոնք ավտոմատ կերպով փոխարկում են պրիմիտ տվյալները։

    // initializing empty list of Integer objects
    LinkedList<Integer> linkedList = new LinkedList<>();
    
    // add int elements explicitly boxing int to Integer
    for (int i = 1; i <= Math.pow(2, 16); i++)
        linkedList.add(i);
    
    
    // initializing empty map of key-value
    HashMap<String, Boolean> hashMap = new HashMap<>(); 
    
    // put key-value pairs
    hashMap.put("p1", true); 
    hashMap.put("p2", false); 
    hashMap.put("p3", true);
    

    Արդեն նշված գործիքները բավարար են ցանկացած հավաքածու ստեղծելու համար, սակայն ավելի արդյունավետ է վերաօգտագործման մոտեցումը։ Java API-ի կողմից տրամադրվում են բազմաթիվ պատրաստի հավաքածուներ, որոնք հավաքված են java.utils փաթեթում։ Յուրաքանչյուր հավաքածուի տեսակ օպտիմալ է որոշակի խնդիրների լուծման պարագայում։

  • Synchronous vs asyncronous

    Ծրագրային կատարման ընթացքի սովորական տրամաբանությունը «սինխրոն» է (synchronous)։ Կատարումը համարվում է սինխրոն, երբ այն հաջորդական է մեկ շղթայական կատարման (thread, поток ռուս․, հոսք հոմ․) սահմաններում, այսինքն՝ հաջորդ կատարմանն անցնելու համար պետք է սպասել ընթացիկ կատարման ավարտին։ Օրինակ՝ ցանկացած I/O գործարկման պարագայում (storage, network, etc.)։ Հոսքերը, ի տարբերություն պրոցեսների, միմյանց հետ կիսում են հատկացված հիշողությունը, այսինքն՝ միևնույն տվյալը հասանելի կարող է լինել տարբեր կատարումների համար։

    Ի տարբերություն սինխրոն կատարման՝ ասինխրոն (asynchronous) կատարումը ենթադրում է միևնույն ծրագրի մեկից ավելի հոսքեր (threads), որոնք միմյանց կատարման ավարտին սպասելու կարիք չունեն։ Զուգահեռ շղթաները կարող են կատարվել բարձր հաճախականությամբ մեկից մյուսին անցնելով և ստեղծել համաժամանակյա (տես՝ հաջորդող Attention բլոկ) կատարման էֆեկտ։ Բազմամիջուկ պրոցեսորների պարագայում տարբեր հոսքեր կարող են ֆիզիկապես կատարվել միաժամանակ։

    Սինխրոն և ասինխրոն կատարումների տերմինաբանության մեջ կա ակնհայտ տարընթերցում։

    Տեղեկատվական տեխնոլոգիաներում «սինխրոն» (synchronous) է կոչվում ժամանակի կտրվածքում մեկ հոսքով և խիստ հաջորդականությամբ կատարումը, և ոչ՝ համաժամանակյա։ ԵՎ հակառակը՝ «ասինխրոն» (asynchronous) են կոչվում հիմնական հոսքից (main thread) անջատ կատարումները։

    Կախված խնդրից՝ կարիք է առաջանում ստեղծել անջատ հոսքեր, կամ հակառակը՝ պահպանել խիստ հաջորդականություն։

    Առաջին դեպքում, օրինակ, I/O գործարկումները կարող են կատարվել առանձին հոսքերով, և, այդպիսով, ազատել հիմնական կատարման հոսքը, քանի որ նման գործարկումները սովորաբար ժամանակատար են լինում և կախված են արտաքին ռեսուրսներից։ Մեկ այլ օրինակ է մեծ զանգվածների սորտավորումը և ֆիլտրացիան, որոնք կարելի է կատարել «զուգահեռ» (parallel)։ Նման դեպքերում ստեղծվում են առանձին հոսքեր, որոնք կատարման ավարտից հետո վերադարձնում են կատարման արդյունքը այն հոսքին, որից ճյուղավորվել են։

    Երկրորդ դեպքում տվյալները կարող են փոփոխվել խիստ հաջորդաբար՝ անկանխատեսելի հետևանքներ չունենալու համար։ Զանգվածների և հավաքածուների դեպքում, բարեբախտաբար, կան ասինխրոն կատարման համար նախատեսված օրինակներ, որոնք սովորաբար ունենում են "Concurrent", "Synchronized", "Atomic" կամ այլ ընդգծված նախածանց, օրինակ՝ ConcurrentHashMap, SynchronizedRandomAccessList, AtomicReferenceArray, և այլն։

    Պրոցեսորի միջուկների «բազմահոսք» (multi-thread) տեխնոլոգիան նույնպես հաճախ շփոթություն է առաջացնում՝ մինչդեռ դրանք տարբեր հասկացություններ են։

    • Synchronized methods

      Մեթոդները նույնպես կարող են հայտարարվել «սինխրոնիզացված»՝ synchronized և ապահովել տարբեր հոսքերի (threads) կողմից տվյալ մեթոդի խիստ հաջորդական կատարումը։

      synchronized public void write() {
          // ... do synchronized
      }
      

      Եթե սինխրոնիզացման ենթակա է մեթոդի միայն մի մասը՝ այն կարող է առանձնացվել սինխրոնիզացված բլոկի մեջ։

      public void write() {
          // ... do some stuff
      
          synchronized {
              // ... do synchronized
          }
      }
      
  • Stream

    (հոսք անգլ․, չշփոթել կատարման շղթայի հետ) Զանգվածների և հավաքածուների հետ աշխատելու գործիք, որը թույլ է տալիս օգտագործել անանուն ("lambda") ֆունկցիաներ և գործողությունները խմբավորել շարահյուսական տեսանկյունից հաջորդական ու ընթեռնելի հրամաններով՝ պահպանելով, միաժամանակ, «ծույլ» կատարում (lazy evaluation) և ռեսուրսների օպտիմալ օգտագործում։

    List<Models> listOfModels = listOfAllSmartphones.stream()
          .map(Smartphone::getModel)         // method reference
          .distinct()                        // filter duplicates
          .collect(Collectors.toList());     // collect
    
    long cheapSmartphones = listOfModels.stream()
          .filter(m -> m.getPrice() <= 200)  // filter by price
          .count();                          // count
    

    Stream ստեղծելուց կարելի է հայտարարել այն «զուգահեռ» parallelStream մեթոդի օգնությամբ։

  • Exception handling

    Բացառությունները (exceptions) կոդի տեսանկյունից լինում են երկու տեսակի՝ ստուգման ենթակա (checked) և ոչ ենթակա (unchecked)։

    Վերջինները նույնպես կարող են ստուգվել, սակայն ծրագրավորողի կողմից սովորաբար անտեսվում են, քանի որ տեղի են ունենում կատարման փուլում և, ըստ էության, անկանխատեսելի (ամբողջ կոդը ստուգումներով ծածկելու փոխարեն կարելի ստուգել կոդը)։ օրինակ՝ NullPointerException, ArrayIndexOutOfBound, IllegalArgumentException, և այլ։

    Ստուգման ենթակա բացառությունները ստուգվում են կազզման ընթացքում, իսկ կառավարումը կատարվում է երկու եղանակով՝ բացառություն(ներ)ը «որսալու» փորձով (try-catch) կամ հայտարարելով։

    • Try-catch

      Բացառությունները «որսալու» նպատակով՝ պոտենցյալ բացառություն առաջացնող հատվածները պարփակվում են try բլոկով, որին անմիջապես պետք է հաջորդի catch բլոկը՝ ակնկալվող բացառությունների հայտարարմամբ (տվյալ օրինակում՝ IOException), որտեղ նշվում են բացառության դեպքում կատարվող գործողությունները։ Մեկից ավելի բացառությունների պարագայում կարելի է հայտարարել նաև առանձին catch բլոկեր։

      byte[] data = stringData.getBytes();
      Path path = Paths.get("./testfile.txt");
      
      OutputStream out = null;
      try {
          out = Files.newOutputStream(p, CREATE, APPEND);
          out.write(data);
          // ... do some stuff
      
          out.close()           // close file (I/O resource)
      
      } catch (IOException e) {
          // ... do some logging
      
          e.printStackTrace();  // print stacktrace in stderr
      }
      

      Վերը նշված օրինակում առաջանում է ևս մեկ խնդիր։ Հարկավոր է ապահովել ֆայլային ռեսուրսի փակումը, քանի որ try բլոկում մինչև ռեսուրսի փակումը բացառություն առաջանալու դեպքում կատարումն անմիջապես կանցնի catch բլոկ և տվյալները չեն պահպանվի։ Այդ դեպքում կարելի է օգտագործել ոչ պարտադիր finally բլոկը, որը կատարվում է բոլոր դեպքերում։ Այնուամենայնիվ, ֆայլային ռեսուրսները փակելը ենթակա է նոր բացառության ստուգման, և, հետևաբար, պետք է պարփակվի ևս մեկ try-catch բլոկով։

      OutputStream out = null;
      try {
          out = Files.newOutputStream(p, CREATE, APPEND);
          out.write(data);
          // ... may cause another EXCEPTION
      
          // out.close() is UNREACHABLE
      
      } catch (IOException e) {
          // ... do some logging
      
          e.printStackTrace();  // print stacktrace in stderr
      
      } finally {
          // check I/O resourses
          if (out != null) {
              try {
                  out.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }
      

      Ավելի ճկուն շարահյուսությամբ լեզուները (Java 7+) թույլ են տալիս ռեսուրսները հայտարարել հատուկ բլոկում, որն ապահովում է հավելյալ finally բլոկի ֆունկցիան և կատարվում է բոլոր դեպքերում։

      // resourse block (resource-block)
      try (OutputStream out = Files.newOutputStream(path, CREATE, APPEND)) {
      
          out.write(data);
      
      } catch (IOException e) {
          // ... do some logging
      
          e.printStackTrace();  // print stacktrace in stderr
      }
      
    • Բացառության հայտարարում

      Ակնկալվող բացառությունները կարող են չպարփակվել try-catch բլոկով, եթե հայտարարվում են որպես պարփակող մեթոդի մաս։ Տվյալ բացառությունները «որսալու» պարտավորությունն այս դեպքում ուղղակիորեն հանձնվում է մեթոդը շահագործող կողմին։

      private void writeToFile(String stringData) throws IOException {
      
          // ... do some stuff
      }
      

3.7 Պրակտիկ (Java)

[2020-09-14]

Նպատակն է ստեղծել մոդուլային հավելված, որի լավագույն գործիքներից է օբյեկտ֊օրիենտացված ծրագրավորումը։ Հավելվածը ստեղծվելու է հնարավոր զարգացման և վերաօգտագործման կոնտեքստում։ Այդ պատճառով նախագծումը պետք է կատարվի տվյալների (data, model) և արտաբերման գործիքների (view) առանձնացման տրամաբանությամբ։

Հավելվածի կոդին առցանց կարելի է ծանոթանալ հետևյալ հասցեով՝ GitHub։
Հավելվածը կարելի է նաև «կրկնօրինակել» (git clone) համապատասխան գործիքներով կամ ներբեռնել որպես դասընթացների ընդհանուր պրոյեկտ (project)` GitHub:

Հավելվածը «փաթեթավորվում» է (build, package) Maven ծրագրով՝ հավելվածի հիմքային (կողմնորոշիչ՝ pom.xml) հասցեում mvn package հրամանի կատարմամբ։ Փաթեթավորման հաջողված ավարտից հետո ստեղծվում է ./target/<file-name>.jar արխիվային ֆայլ, որն իրենից ներկայացնում է կազմված (compiled) ամբողջական հավելված։ Հավելվածը գործարկելու համար հարկավոր է կատարել java -jar <file-name>.jar հրամանը։

Հետևյալ հղումով կարող եք ներբեռնել նախապես փաթեթավորված հավելվածը - JAR։

  • Հղում ծանոթության համար - Enum, Annotations, String Formatting

3.8 Մուլտիպարադիգմ մոտեցում (Python)

[2020-09-16]

Python ծրագրավորման լեզուն ի սկզբանե նախագծվել է որպես «մեկնաբանող» մեքենայի` Python Interpreter, և ընդհանուր կիրառման մուլտիպարադիգմ լեզվի համալիր։

Կան Python լեզվով ծրագրավորման տարբեր (նաև տարբեր միջավայրերում աշխատող) ինտերպրետատորներ, սակայն դասական և հիմնական տարբերակը CPython ինտերպրետատորն է, որն օֆիցյալ Python փաթեթի մասն է կազմում։ Ինտերպրետացիոն մեխանիզմները վիրտուալ մեքենայի աշխատանքից տարբերվում են կատարմամբ։ Վիրտուալ մեքենան ենթադրում է նախապես ստուգված և կազմված (compiled) հրահանգների կոդ, մինչդեռ ինտերպրետատորը ուղղակիորեն գործում է ըստ իր անվանման՝ հերթականությամբ մեկնաբանելով գրված կոդը։ Այս մոտեցումը գրեթե ամբողջությամբ բնորոշում է ծրագրավորման լեզվի առանձնահատկությունները։

Python 2 տարբերակը 2020 թվականից համարվում է «դադարեցված», այդ պատճառով օրինակները բերվում են միայն 3֊րդ տարբերակի համար։

  • Հիմնական առանձնակատկությունները
    • Տիպիզացիա

      Այստեղ տիպիզացիան դինամիկ է և խիստ, այսինքն՝ չի հայտարարվում և ստուգվում է միայն կատարման պահին, սակայն տիպային անհամապատասխանությունների պարագայում (բացառությամբ նախորոշված դեպքերի) տեղի է ունենում կատարման խափանում՝ տիպային բացառություն TypeError: Բարդ կառուցվածքով տվյալները համարվում են նույնը, եթե ունեն նույնատիպ կառուցվածք ֊ Duck typing:

      Առաջին հայացքից թվացող առավելությունը բավական խափուսիկ է, և «խնայված» տառանշանների գինը վաղ թե ուշ վճարվում է համաչափ, իսկ պրակտիկորեն՝ անհամաչափ, ռեսուրսներով։ Այդ իսկ պատճառով, բազմաթիվ հեռուստադիտողների համայնքի (community) խնդրանքով, 3.5 տարբերակում ավելացվեցին տիպային «հուշումներ»՝ type hints։

    • Խիստ հերթական կատարում

      Մեկնաբանմամբ կատարումը ստիպում է նախագծել գծային տրամաբանությամբ։ Փոփոխականներին և ֆունկցիաներին հնարավոր չէ հղում անել մինչև դրանց հայտարարումը։ Հետևյալ կոդը կխափանի կատարումը, քանի որ y փոփոխականին հղումը, ըստ կատարման, տեղի է ունենում այն ժամանակ, երբ նման փոփոխական դեռ հայտարարված չէ։

      x = y + 42        # NameError
      y = 5
      

      Նույնը տեղի է ունենում ֆունկցիաների հայտարարման պարագայում, քանի որ say_hello() ֆունկցիային հղում է արվում մինչև նրա հայտարարումը։

      # function call
      say_hello()       # NameError
      
      # function declaration
      def say_hello():
          print("Hello")
      
    • Միջակայք (scope)

      Ի տարբերություն ուլտիմատիվ օբյեկտ֊օրիենտացված լեզուների, որտեղ ցանկացած արտահայտություն մաս է կազմում որևէ կլասի և գտնվում է վերջինի միջակայքում, այստեղ արտահայտությունները կարող են ներդրված չլինել և կարող են կատարվել որպես հաջորդական հրահանգներ։ Միջակայքի ներդրվածության աստիճանը որոշում է ձախ լուսանցքի նկատմամբ խորության աստիճանը (indent, отступ ռուս․), որը պարտավոր է լինել նախորոշված միավորին պատիկ ամբողջ կոդի համար, օրինակ՝ չորս բացատ։

      Միջակայքերը դինամիկ տիպիզացման պարագայում առաջացնում են հավելյալ խնդիր, երբ փոփոխականի հայտարարումը և փոփոխումը շարահյուսության տեսանկյունից չեն տարբերվում։ Եթե ստատիկ տիպիզացման դեպքում հայտարարումը ընդգծվում էր շարահյուսությամբ՝

      int num = 5;    // declare and assign a variable
      
      num = 42;       // assign new value
      

      Ապա դինամիկ տիպիզացման պարագայում փոփոխականի հայտարարումն ու փոփոխումն ակնհայտ չեն։ Հետևյալ կոդում ֆունկցիան հայտարարում է իր ներսում num անունով ամբողջովին նոր փոփոխական` առանց փոփոխելու նախկինը։

      num = 5         # declare a variable
      
      def print42():
          num = 42    # declare another variable
          print(num)
      
      print42()
      print(num)
      
      42
      5
      

      Ընդհանուր (գլոբալ) միջակայքում հայտարարված փոփոխականի արժեքը փոխելու համար հարկավոր է նախապես հայտարարել տվյալ փոփոխականը որպես գլոբալ՝ օգտագործելով համանուն «բանալի֊բառ» (keyword) global:

      num = 5          # declare a variable
      
      def print42():
          global num   # declare variable global
          num = 42     # assign new value
          print(num)
      
      print42()
      print(num)
      
      42
      42
      
    • Առաջին կարգի ֆունկցիա

      Առաջին կարգի (first-class) ֆունկցիաները ցանկացած ծրագրավորման լեզվում ամենաարդյունավետ գործիքներից են և տալիս են մի շարք «նոր» և ճկուն հնարավորություններ։ Ֆունկցիաները և ֆունկցիոնալ գործիքները դիտարկվում են հաջորդ դասընթացի ընթացքում։ Առաջին կարգի ֆունկցիաները առավել ցայտուն դիտարկվում են Ֆունկցիոնալ ծրագրավորմանը վերաբերվող դասընթացների ընթացքում։

    • Անմիջական և հետաձգված կատարում

      Հետաձգված մոդելներից են գեներատորները, որոնք հաճախակի են հանդիպում ժամանակակից լեզուներում։ Այդպես, օրինակ լեզվի մաս կազմող (built-in) range ֆունկցիան չի ստեղծում զրոյից մինչև մեկ միլիարդ միջակայքի բոլոր ամբողջ թվերի զանգվածը։ Փոխարենը, փոփոխականին է հանձնում «հետաձգված կատարում», որը կարող է հաջորդող կոդում տեղի ունենալ միայն մասնակի։

      lots_of_integers = range(1_000_000_000)   # same as range(0, 1000000000)
      
      print(lots_of_integers[42])               # goes until 42 and stops
      
      42
      
    • Օպերատորներ

      Python լեզուն հաճախ խորհուրդ են տալիս որպես երեխաների առաջին ծրագրավորման լեզու, քանի որ արտահայտման մեխանիզմներն այստեղ շատ ավելի պարզեցված են, քան C լեզվաընտանիքի լեզուներում։ Օրինակ՝ &&, ||, ! օպերատորների փոխարեն գործում են, համապատասխանաբար, and, or, not օպերատորները։ Հետևյալ օրինակում երևում է նաև for ցիկլերի պարզեցման աստիճանը։

      collection = [8,-6,24,9]
      
      # for-each loop
      for var in collection:
          print(var)
      
      8
      -6
      24
      9
      
  • Կառուցվածք

    Ֆայլը տարբեր լեզուներում կարող է հանդես գալ որպես ծրագրային կառուցվածքի որոշակի բաղադրիչ, ինչպես, օրինակ, Java լեզվում ֆայլը ներկայացնում էր մեկ կամ ավելի կլաս։ Python լեզվում ֆայլը հանդես է գալիս որպես մոդուլ (module)։ Մոդուլը իր մեջ կարող է պարունակել. 1) գլոբալ միջակայքի հրահանգներ, 2) ֆունկցիաներ և 3) կլասեր։

    Մոդուլներն իրենց հերթին ֆայլային համակարգում խմբավորվում են որպես «փաթեթ» (package)։ Python 3.3 և ավելի վաղ տարբերակներում ֆայլային համակարգի հասցեն (directory) համարվում է փաթեթ, եթե տվյալ հասցեում առկա է __init__.py ֆայլ՝ որպես կողմնորոշիչ (բովանդակությունը պարտադիր չէ)։ Այնուամենայնիվ, նոր տարբերակներում այս ֆայլը կարող է օգտակար լինել փաթեթի մակարդակում ընդհանուր տվյալներ ունենալու տեսանկյունից, քանի որ ներառված կոդի դեպքում այն բեռնավորվում է փաթեթում գտնվող բոլոր մոդուլների (ֆայլերի) բեռնավորման կոնտեքստում և գործում է որպես ընդհանուր կոդ։

    • Import

      Մոդուլները ներմուծվում են import արտահայտությունների օգնությամբ։

      import sys                # import module
      import os as os_          # import module renamed
      from math import sin, pi  # import specific part(s) directly
      from random import *      # import all parts directly
      
      
      print(sys.platform)
      print(os_.cpu_count())
      print(sin(pi/2))
      print(randint(50, 100))
      
      linux
      12
      1.0
      80
      
    • Հատուկ նշագրում (notation)

      Ընդգծման նշագրումը (underscore notation) Python լեզվում ունի հատուկ նշանակություն։ Այդպիսի անվանակոչումների հիմնական նպատակը համակարգի (ներքին) օգտագործման անվանումների (ֆայլեր, փոփոխականներ, etc.) և ծրագրավորողի կողմից ստեղծվող անվանումների հնարավոր «ընդհարման» (collision) բացառումն է։ Կան նաև հատուկ նշումներ կլասի ատրիբուտների համար, քանի որ կլասի բաղադրիչները «պաշտպանված» չեն private, protected կամ այլ կարգավորիչներով։

      Կլասի կառուցվածքը դիտարկվում է հաջորդող դասընթացի ընթացքում։

      _name Ներքին օգտագործման նշան, որը հիմնականում անտեսվում է ինտերպրետատորի կողմից (բացառությամբ from <module> import * կառուցվածքով ներմուծման դեպքերից)։
      name_ Բանալի֊բառերի (keywords) հետ ընդհարումից խուսափելու միջոց։
      __name Triggers name mangling when used in a class context. Enforced by the Python interpreter.
      __name__ Համակարգի (ներքին) օգտագործման անվանումների հետ ընդհարումից խուսափելու միջոց։

      Եզակի ընդգծման նշանը առանց տառանշանների (_) օգտագործվում է որպես ժամանակավոր անանուն փոփոխական, որի արժեքի տվյալ կոնտեքստում չի օգտագործվում: Նաև օգտագործվում է ինտերակտիվ տերմինալում որպես նախորդ արտահայտության արտաբերման փոփոխական։

      for _ in range(limit):
          # do some stuff iterating
      
      >>> 2 + 2
      4
      >>> 3 + _
      7
      
  • Հիմնական տվյալների տիպեր
    • Numbers

      Թվային արժեքները դինամիկ են տիպիզացված, սակայն ունեն որոշ առանձնահատկություններ․

      • Ամբողջ թվերը չունեն մեքենայական բիթային դասակարգում և սահմանափակվում են միայն հիշողությամբ (անդրադառնալով, բնականաբար, արագագործության վրա),
      • Բաժանման համար կա երկու օպերատոր` / - միշտ տասնորդական արդյունքով, և // - միշտ ամբողջ թվի արդյունքով (կրճատելով տասնորդական մասը),
      • Թիվը աստիճան է բարձրացվում հիմքը և ցուցիչը կրկնակի բազմապատկման նշանի օգնությամբ անջատելով՝ 2**8 = 256:

      Հանրահաշվական օպերատորները, կախված տվյալներից, կարող են լինել նաև պոլիմորֆ։ Ոչ թվային տվյալների նկատմամբ նույնպես կարելի է կիրառել որոշ օպերատորներ։

      >>> 2 + 2.4
      4.4
      
      >>> 2 + 40
      42
      
      >>> 2 + "40"
      TypeError
      
      >>> 5 // 2       # truncate decimal part
      2
      
      >>> 6 / 3        # always decimal
      2.0
      
    • Lists

      Զանգվածների դերը կատարում է մեկ ընդհանուր list տվյալի տիպը։ Լիստերն ունեն դինամիկ ծավալ և փոփոխվող բովանդակություն։ Տիպիզացիան նույնպես դինամիկ է, սակայն նման մոտեցում սովորաբար խորհուրդ չի տրվում՝ անկանխատեսելի վարքագիծ առաջացնելու պատճառով։ Լիստերը հայտարարվում են քառակուսի չակերտների [] օգնությամբ։

      Լիստերի ինդեքսացիան սկսվում է 0-ից, իսկ էլեմենտների հասանելիությունը կատարվում է զանգվածներին բնորոշ եղանակով։ Նոր էլեմենտներ կարելի է ավելացնել append (վերջում) կամ insert (նշված ինդեքսով) ֆունկցիաների օգնությամբ՝ որպես լիստ տվյալ պարունակող փոփոխականի «մեթոդ»։ Ինդեքսային ցուցիչը կարող է լինել բացասական արժեք, օրինակ՝ վերջին էլեմենտի համար կարելի է նշել [-1] ինդեքս։

      lst  = [1,2,3,4,5,6,7]
      newLst = lst
      
      newLst[0] = "one"
      
      print(lst)
      
      ['one', 2, 3, 4, 5, 6, 7]
      

      Լիստերը նույնպես ենթարկվում են որոշ հանրահաշվական գործողությունների։

      >>> [1,2] + [3,4,5,6,7]   # concatenation
      [1,2,3,4,5,6,7]
      
      >>> 3 * [1,2,3]           # concatenation
      [1,2,3,1,2,3,1,2,3]
      
    • Strings

      Տեքստային տվյալներն իրենցից ներկայացնում են տառանշանների (character) հերթականություն։ Նաև որպես փոփոխական կարող են կրել բայթերի հաջորդականություն։ Տառանշանների առանձին տիպ չկա, տառանշանը «մեկ տառ» ծավալով տեքստն է։

      Տեքստային տվյալների հետ կարելի է աշխատել ինչպես ստանդարտ հավաքածուների (list), այն տարբերությամբ, որ տեքստային տվյալները բովանդակությամբ անփոփոխ են (immutable)։ Տեքստային տվյալները կարելի է հայտարարել եզակի ' կամ կրկնակի " չակերտներով։ Չակերտներից մեկով նշելու դեպքում՝ մյուսը անտեսվում է, և հատուկ կարգավորիչ նշանի (escape character) կարիք չի առաջանում։

      s = "String declaration"
      
      # s[0] = "X"  -  produces TypeError (strings are immutable)
      
      dChar = s[7]
      
      print('7th lement of "s" is ' + dChar)  # no escape
      
      7th lement of "s" is d
      

      Տեքստային տվյալները նույնպես ենթարկվում են որոշ հանրահաշվական գործողությունների։

      >>> "Hello " + "Ajapnyak!"   # concatenation
      Hello Ajapnjak!
      
      >>> 3 * "Hooray! "           # concatenation
      Hooray! Hooray! Hooray! 
      
    • Tuples

      Որոշակի քանակով և հերթականությամբ տվյալների խումբ, որը հայտարարվում է սովորական փակագծերի օգնությամբ և, ի տարբերություն լիստերի, բովանդակությամբ անփոփոխ է (immutable)։

      t = (0, 'One', True)
      
      # t[0] = 1  -  produces TypeError (tuples are immutable)
      
      print(t[1] + ", Two, Three...")
      
      One, Two, Three...
      
    • Dictionaries

      Բանալի֊արժեք (key-value) զույգերի դասական հավաքածու, որտեղ արժեքի հասանելիությունը ապահովում է չկրկնվող (unique) բանալին։

      d = {'a': 97, 'b': 98}
      print(d)
      
      d['c'] = 99
      print(d)
      
      d['a'] = 1000
      print(d)
      
      {'a': 97, 'b': 98}
      {'a': 97, 'b': 98, 'c': 99}
      {'a': 1000, 'b': 98, 'c': 99}
      
    • Sets

      Չկրկնվող էլեմենտների հավաքածու առանց որոշակի հերթականության և ինդեքսացիայի։ Բացի սեփական մեթոդներից՝ ենթարկվում է հանրահաշվական և բուլյան որոշ գործողությունների։

      s = {'a', 'b', 'c'}
      s.add('d')
      
      print(s)
      print({1,2,3,4} and {2,4,6})
      print(s - {'a','b'})
      
      {'b', 'a', 'd', 'c'}
      {2, 4, 6}
      {'d', 'c'}
      

      Դատարկ փակագծերով s = {} հայտարարումը ստեղծում է dictionary հավաքածու։

    • Booleans

      Բուլյան արժեքները կրկնում են դասական մոտեցումը, սակայն գրվում են գլխատառ՝ True և False:

      Բոլոր թվային, տեքստային և հավաքածուների տիպի օբյեկտները կարող են ենթարկվել պայմանային (consitionel) ստուգման՝ որպես բուլյան տիպ։ Զրոյական արժեքով թվային տվյալները և զրոյական ծավալով հավաքածուները (0, 0.0, "", [], {}) դիտարկվում են որպես False, բոլոր այլ արժեքների դեպքում` True:

      n = -256
      l = ['a', 'b', 'c']
      
      if n:
          print("n != 0")
      
      if l:
          print("l is not empty")
      
      n != 0
      l is not empty
      

      Այնուամենայնիվ, True և False հատուկ անունները բանալի֊բառեր են և հավասար են համապատասխանաբար 1 և 0 արժեքների, այդ իսկ պատճառով 5 == True արտահայտությունը «ճիշտ չէ» (False)։

  • Built-in helpers

    Python ինտերպրետատորի միջավայրում նախապես ներբեռնված են բազմաթիվ ֆունկցիաներ, որոնք օգնում են ինտերակտիվ տերմինալում ներբեռնված փոփոխականները և ֆունկցիաները տեսնելու հարցում։

    dir( name ) Արտաբերում է նախօրոք բեռնված և ներմուծված բոլոր մոդուլները և փոփոխականները
    type( name ) Արտաբերում է փոփոխականի տիպը և տեսակը (օր. ֆունկցիա լինելը)
    help( name ) Արտաբերում է դոկումենտացիան, եթե այդպիսին առկա է

3.9 Ֆունկցիոնալ գործիքներ (Python)

[2020-09-18]

Ֆունկցիաները մուլտիպարադիգմ լեզուներում սովորաբար լինում են «ազատ» (free functions) կամ որևէ կլասի անդամ (մեթոդներ)։ Ներդրված (nested) և անանուն ֆունկցիաներն, իրենց հերթին, հավելյալ ճկունություն են հաղորդում լեզվին։

Արդեն ծանոթ print ֆունկցիան նույնպես ազատ ֆունկցիա է։ Առկա են նաև տիպերի փոխարկման և ինիցիալիզացիայի համանուն ազատ ֆունկցիաներ՝ int(), str(), list(), dict() և այլն, որոնց օբյեկտ֊օրիենտացված լեզուների պարագայում (օրինակ՝ Java) փոխարինում են համանուն կլասերի ստատիկ մեթոդները, օրինակ՝ Integer.parseInt(), String.valueOf() և այլն։

Ազատ ֆունկցիաները և կլասերի մեթոդները մուլտիպարադիգմ լեզուներում ըստ էության կիրառվում են հավասարապես։

l = [4,5,6,7]
print(l)

# use class meber function (method)
l.append(8)
print(l)


# declare a free function
def appendToList(lst, item):
    lst.append(item)

# use free function doing the same
appendToList(l, 9)
print(l)
[4, 5, 6, 7]
[4, 5, 6, 7, 8]
[4, 5, 6, 7, 8, 9]

Ֆունկցիան հայտարարվում է def բանալի֊բառի (keyword) օգնությամբ, որին հաջորդում է ֆունկցիայի անվանումը։ Հայտարարելուց հետո ֆունկցիային կարելի է հղում անել ինչպես փոփոխականի՝ փոխանցել որպես արգումենտ, վերադարձնել մեկ այլ ֆունկցիայից կամ կցել մեկ այլ փոփոխականի։ C լեզվաընտանիքի լեզուների շարահյուսությամբ, որն այս կոնտեքստում ժառանգել է նաև Python լեզուն, ֆունկցիան ենթարկվում է կատարման կամ կիրառման (apply), երբ ֆունկցիայի անվանմանը հաջորդում են փակագծերը՝ համապատասխան արգումենտների փոխանցմամբ կամ առանց արգումենտների։

Որպես արգումենտ փոխանցվող ֆունցիաներն այդ կոնտեքստում կոչվում են callback, եթե կիրառվում են (apply) «ընդունող» ֆունկցիայի ներսում:

def add(x, y):
    return x + y


# get a 'callback' function as an argument
def doArg2(f, a1, a2):
    return f(a1, a2)       # apply 'f'


# if first argument is not a function - TypeError
res = doArg2(add, 7, 8)    # pass 'add' as an argument
print(res)


# assign function to variable
do = doArg2
res = do(add, 12, 15)      # reassign res variable
print(res)
15
27

Python լեզվում առկա են բազմաթիվ ազատ ֆունկցիաներ, որոնք լռելյայն (by default) ներբեռնվում են բոլոր մոդուլներում (ֆայլերում)։ Այդ ֆունկցիաների ցանկը կարելի է տեսնել dir(__builtins__) հրամանն ինտերակտիվ տերմինալ ներմուծելով (ֆունկցիայի, ինչպես և փոփոխականների, անունները սկսվում են փոքրատառ):

Տվյալների և ֆունկցիաների անվանումները, որպես համարժեք «սիմվոլ» (անվանում, որով գրանցվում է հղումը), նշագրմամբ չեն տարբերվում։ Այդ պատճառով կարիք է առաջանում ստուգելու՝ արդյոք սիմվոլը տվյալի հղում է, թե ֆունկցիայի։ Նման դեպքերում կարելի է օգտվել type( symbol ) ֆունկցիայից։

  • Functions and arguments

    Ֆունկցիաներն, առհասարակ, ունեն երեք կարևորագույն հատկանիշ՝ արգումենտներ, վերադարձվող արժեք և էֆեկտ (արտաքին միջավայրի հետ ներգործություն)։ Դինամիկ տիպիզացմամբ լեզուներում ֆունկցիաները տարբերակվում են մեծ հաշվով ըստ ընդունվող արգումենտների քանակի, քանի որ մինչև կատարման պահը տիպային տարբերակում չկա։

    Որևէ արժեք կամ հղում ուղղակիորեն չվերադարձնելու դեպքում ֆունկցիան լռելյայն (by default) վերադարձնում է None, որն իրենից ներկայացնում է հղման և արժեքի բացակայություն (null

    def sayHello():
        print("Hello!")
        # no return statement
    
    
    res = sayHello()
    print(res)
    
    Hello!
    None
    
    • Fixed (positional) arguments

      Արգումենտները սովորաբար հայտարարվում են ուղղակիորեն՝ որպես «ներքին» փոփոխականներ, որոնք կիրառման պահին փոխարինվում են փոխանցված փոփոխականներով (հղումներով)։

      def add3(n1, n2, n3):
          return n1 + n2 + n3
      
      
      res = add3(2, 3, 4)
      print(res)
      
      9
      
    • Named arguments

      Արգումենտները կարող են փոխանցվել կամայական հերթականությամբ, եթե հղում են արվում հայտարարված փոփոխականներին։

      def greet(msg, name):
          print(msg + ", " + name + "!")
      
      
      greet(name="Varazdat", msg="Hello")
      
      Hello, Varazdat!
      
    • Variable number of arguments

      Տարբեր լեզուներում հայտնի է նաև փոփոխական քանակի արգումենտներով ֆունկցիայի հայտարարման տարբերակ։ Python լեզվում դա նույնպես հնարավոր է * նախածանցի օգնությամբ։

      def add(*ns):
          acc = 0
          for n in ns:
              acc += n
          return acc
      
      
      print(add(2, 2018))
      print(add(8, 7, 5, 10, 8, 4))
      print(add())
      
      2020
      42
      0
      

      Փոփոխական քանակով արգումենտները հաջորդում են ֆիքսված արգումենտներին, եթե այդպիսիք հայտարարված են՝ def f(x, y, *args):, հակառակ դեպքում, օրինակ՝ def f(x, *args, y):, հաջորդող արգումենտները պետք է փոխանցվեն միայն անվանական փոփոխականով` f(5, 6, 7, 8, y=9):

    • Variable number of keyword arguments

      Փոփոխական արգումենտները ** նախածանցի օգնությամբ կարող են հայտարարվել նաև բանալի֊արժեք տրամանաբանությամբ՝ փոփոխական քանակով արգումենտների հայտարարմանը հաջորդող, եթե այդպիսիք հայտարարված են։ Նման արգումենտները փոխանցվում են խստորեն անվանապես։

      def dimensions(**obj):
          print("Width: " + obj["w"] + "\nHeight: " + obj["h"])
      
      
      dimensions(w="218mm", h="326mm")
      
      Width: 218mm
      Height: 326mm
      
    • Default arguments

      Արգումենտները կարող են հայտարարվել նաև լռելյայն (default) արժեքով` ֆիքսված և փոփոխական քանակով արգումենտներից հետո, ինչը պրակտիկորեն կարող է փոխարինել ֆունկցիաների վերաբեռնավորումը (overloading)։

      def greet(name, msg="Hello", opt="How are you?"):
          print(msg + ", " + name + "! " + opt)
      
      
      # fixed args only
      greet("Tigran")
      
      # pass 'msg' inplace, 'opt' is default
      greet("Tigran", "Hi")
      
      # 'msg' is default, pass 'opt' by name (otherwise casts 'msg')
      greet("Tigran", opt="")
      
      Hello, Tigran! How are you?
      Hi, Tigran! How are you?
      Hello, Tigran! 
      

      Արդեն հայտնի print ֆունկցիան իրականում հայտարարված է մոտավորապես (բաց թողնելով որոշ դետալներ) հետևյալ կերպ def print(*args, sep=' ', end='\n', file=sys.stdout):, այսինքն՝ կարելի է ուղղակիորեն փոխանցել մեկից ավելի արգումենտեր և փոփոխել լռելյայն արգումենտները, որոնք կարգավորում են համապատասխանաբար՝ մեկից ավելի արգումենտների բաժանարարը, տողի ավարտը (որն արդեն համանման կարգավորում ունի) և արտաբերման ֆայլը (որը կարելի է փոխել ցանկացած այլ ֆայլով)։

      print(1, 2, 3)
      
      print(1, 2, 3, sep=", ")
      
      print(1, 2, 3, sep="")
      
      print("Bye", end=" ")
      print("Bye")
      
      1 2 3
      1, 2, 3
      123
      Bye Bye
      
  • Anonymous functions

    Անանուն ֆունկցիաները lambda արտահայտության շնորհիվ կարող են հայտարարվել անհրաժեշտ տեղում կամ կցվել փոփոխականի՝ դառնալով անվանական։ Անանուն ֆունկցիայի բաժանարարն է : նշանը, որից առաջ հայտարարվում են արգումենտները։ Անանուն ֆունկցիան վերադարձնում է բաժանարար նշանից աջ գտնվող արտահայտության կատարման արժեքը։

    def do(f, a, b):      # get function by argument
        return f(a, b)    # apply function and return result
    
    
    # assign lambda to variable
    mult = lambda x, y: x*y
    
    
    # pass 'mult' as 'f' to 'do' with nums
    print(do(mult, 3, 12))
    
    # pass lambda as 'f' to 'do' with nums
    print(do(lambda x, y: x**y, 2, 8))
    
    36
    256
    
  • Nested functions

    Ֆունկցիոնալ պարադիգմի լեզուներում հաճախ կարելի է հայտարարել նաև ներդրված ֆունկցիաներ։ Ներդրված ֆունկցիաները ուղղակիորեն հասանելի են միայն արտաքին պարփակող ֆունկցիայի համար, եթե չեն վերադարձվում վերջինի կողմից։ Վերաօգտագործման տեսանկյունից ներդրված ֆունկցիաները տարբերվում են յուրօրինակ «ինկապսուլյացիայով»։

    • Closure

      (Замыкание ռուս․) Երբ պարփակող ֆունկցիան վերադարձնում է իր ներքին միջավայրում ներդրված ֆունկցիան որպես սեփական կատարման արդյունք՝ ներդրված ֆունկցիայի հետ միասին անուղղակիորեն դուրս է բերվում նաև ներդրված ֆունկցիան պարփակող միջավայրը։

      # enclosing function
      def outerFn():
      
          # local variable of outer function
          msg = "Hey! "
      
          # nested function
          def innerFn(name):
              print(msg + name)
      
          # outer function returns inner function
          return innerFn
      
      
      # return and assign inner function
      greet = outerFn()
      
      greet("I'm closure!")
      
      Hey! I'm closure!
      

      Տվյալ դեպքում պարփակող «միջավայրում» ընդգրկվում և ներդրված ֆունկցիայի հետ մեկուսացվում են նաև պարփակող ֆունկցիայի արգումենտները։ Հետևյալ մեխանիզմը հետաձգված կատարման և ֆունկցիոնալ կոմպոզիցիայի օրինակ է։

      # 'base' is also bound to closure
      def powerOf(base):
          # inner function may be anonymous
          return lambda p: base**p
      
      
      # composing new functions
      powerOfTwo = powerOf(2)
      powerOfThree = powerOf(3)
      
      
      print(powerOfTwo(8))     # замыкание
      print(powerOfThree(3))   # замыкание
      
      256
      27
      
    • Generators

      Վերը նշված մեխանիզմները կարող են ստեղծել նաև յուրoրինակ գեներատորներ, որոնք պահպանում և, միաժամանակ, մեկուսացնում են իրենց կցված վիճակը (state)։

      Ներքին ֆունկցիայից պարփակող ֆունկցիայի փոփոխականը փոփոխելու համար հարկավոր է օգտագործել nonlocal բանալի֊բառը, հակառակ դեպքում վերահայտարարելու փորձ է արվելու։ Հետևյալ օրինակում վիճակի (state) փոփոխումը կատարվում է state += 2 արտահայտությամբ, որը համարժեք է state = state + 2 արտահայտությանը։ Հետևաբար արտահայտությունը կառաջացնի UnboundLocalError բացառություն՝ ընդգծելով, որ state փոփոխականին հղում է արվում մինչև հայտարարելը:

      def makeEvenGenerator():
      
          state = 0
      
          def nextEven():
              nonlocal state
              state += 2
              return state
      
          return nextEven
      
      
      # make a generator
      evenGen = makeEvenGenerator()
      
      print(evenGen())               # next state
      print(evenGen())               # next state
      print(evenGen())               # next state
      print(evenGen(), end="\n\n")   # next state
      
      
      # make another generator
      evenGen_ = makeEvenGenerator()
      
      print(evenGen_())              # next state of new generator
      
      
      2
      4
      6
      8
      
      2
      

      Կան գեներատորների և իտերատորների ստեղծման այլ օրինակներ, որոնք կդիտարկվեն հաջորդող դասընթացներում։


3.10 Այլ պրակտիկ գործիքներ (Python)

[2020-09-23]

«Բարձր մակարդակի» դինամիկ տիպիզացմամբ լեզուներն ըստ էության օժտված են մի շարք շարահյուսական և իմաստաբանական առավելություններով, որոնք փորձում են փոխհատուցել կատարման արագագործությունը։

  • Yield

    Նախորդ դասընթացում դիտարկված գեներատորները, որոնք ստացվում էին ներդրված ֆունկցիաների օգնությամբ, ունեն ևս կառուցման ավելի կանոնիկ եղանակ։ Այս դեպքում հիմնական ֆունկցիան, առանց ներդրված ֆունկցիայի առկայության, արդյունքը վերադարձնում է yield օպերատորի օգնությամբ և պահպանում է ընթացիկ վիճակը։

    def makeEvenGenerator():
    
        state = 0
    
        while True:
            state += 2
            yield state        # no return statement
    
    
    # make generator object
    g = makeEvenGenerator()
    
    print(type(g))
    
    # use 'next' function to generate next value
    print(next(g))
    print(next(g))
    print(next(g))
    
    <class 'generator'>
    2
    4
    6
    

    Այս եղանակով ստացված գեներատորները կարելի է լիարժեք կիրառել ցիկլերում։

    def makeEvenRange(n):
    
        state = 0
    
        while state < n:
            state += 2
            yield state        # no return statement
    
    
    # make generator object
    for i in makeEvenRange(18):
        print(i, end=" ")
    
    2 4 6 8 10 12 14 16 18 
    
  • Sequences

    Հաջորդական տրամաբանությամբ տվյալները, օրինակ՝ Լist և String), ունեն շարահյուսական մի շարք ճկուն մեխանիզմներ։

    • Сomprehensions

      Լիստերը կարելի է գեներացնել ներդրված արտահայտությունների օգնությամբ` <statement> <for loop> <optional conditionel>:

      # creates list from 0 to 9 
      r = [x for x in range(10)]
      
      
      # creates hal-of-evens list from 0 to 15 
      d = [x/2 for x in range(16) if x % 2 == 0]
      
      
      # 2d list generation - a la TicTacToe
      t = [[x for x in range(3)] for y in range(3)]
      
      print(type(r), r)
      print(type(d), d)
      print(type(t), t)
      
      <class 'list'> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
      <class 'list'> [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]
      <class 'list'> [[0, 1, 2], [0, 1, 2], [0, 1, 2]]
      

      Նույն արտահայտությունները հասարակ փակագծերի () մեջ առաջացնում են «հետաձգված կատարմամբ» գեներատորներ:

      # generate square-of-evens list from evens from 0 to 11 
      gen = (x**2 for x in range(12) if x % 2 == 0)
      
      print(type(gen))
      for i in gen:
          print(i, end=" ")
      
      <class 'generator'>
      0 4 16 36 64 100 
      

      Կախված իրականացման տրամաբանությունից՝ միջակայքի հիման վրա հավաքածուներ ստեղծող ֆունկցիաները կարող են ներառել կամ չներառել նշվող արժեքները։ Օրինակ՝ range ֆունկցիան չի ներառում միջակայքի «վերջնական» ինդեքսը (stop index), այդ պատճառով range(10) կիրառումը ստեղծում է 0֊ից մինչև 9 թվերի հերթականությունը։

    • Slices

      Հավաքածուներից որևէ «կտրվածք» ստանալու կամ տեղում փոփոխելու համար կարելի է օգտագործել հատուկ նշագրում, որը թույլ է տալիս քառակուսի փակագծերի մեջ ինդեքսի փոխարեն նշել միջակայք, որը նույնպես չի ներառում «վերջնական» (stop) ինդեքսի արժեքը։ Բացասական արժեքի դեպքում ինդեքսացիան հաշվի է առնվում վերջից՝ հակառակ ուղղությամբ։ Նոր կտրվածքները փոխանցվում են որպես նոր տվյալ և ելակետային հղում չեն պահպանում, սակայն կարող են օգտագործվել որպես հղում տվյալների փոփոխման համար։

      l = [1,2,3,4,5,6,7]
      
      # create new list by slice
      s = l[2:4]
      r = l[2:-3]
      
      print(s)
      print(r)
      
      # modify initial list by slice
      del l[5:]
      
      print()
      
      [3, 4]
      [3, 4]
      

      Միջակայքը կարելի է նշել բաց թողնելով «սկզբնական» ([:i]) կամ «վերջնական» ([i:]) արժեքները։ Այդ դեպքում բաց թողնված արժեքների փոխարեն ենթադրվում են համապատասխանաբար «սկզբնական» (start) և «վերջնական» (stop) ինդեքսները։ Առանց միջակայքի նշման ([:]) կարելի է ամբողջությամբ կրկնօրինակել հավաքածուն որպես նոր տվյալ։

      s = "Telephone"
      
      print(s[-5:])
      
      phone
      

      Միջակայքին կարելի է ավելացնել նաև «քայլերի» արժեք՝ ավելացնելով ևս մեկ : բաժանարար։ Այս դեպքում ֆիլտրվում են միայն երրորդ ցուցիչին համապատասխան արժեքները։ Երրորդ արգումենտի բացասական լինելու դեպքում քայլերը հաշվառվում են վերջից՝ հակառակ ուղղությամբ։

      l = [x for x in range(24)]
      
      p = "AREPO"
      
      print(l[1:10:2])     # filter odds up to 10
      print(p[::-1])       # make reverse
      
      [1, 3, 5, 7, 9]
      OPERA
      
  • Multivariable statements

    Միևնույն մեկնարկային արժեքով փոփոխականներ կարելի է հայտարարել «շղթայական» հայտարարման ձևաչափով։ Քանի որ = նշանն ունի աջ ասոցիատիվություն՝ տվյալ հայտարարումը համարժեք է հերթական հայտարարմանը։

    x = y = z = 0
    
    # Same as...
    # z = 0
    # y = z
    # x = y
    

    Փոփոխվող (mutable) տվյալների դեպքում (օրինակ՝ list կամ dict) հղում է կատարվում միևնույն տվյալին, նույնիսկ եթե տվյալը հայտարարվել է առանց բովանդակության։ Հետևաբար՝ փոփոխականներից մեկի օգնությամբ տվյալի բովանդակությունը փոփոխելիս փոխվում է մեկ ընդհանուր տվյալ բոլոր հղումների համար։

    a = b = 0      # assign 'a' and 'b'
    a = 100        # reassign 'a' only ('b' remains 0)
    
    print("a =", a)
    print("b =", b)
    
    
    l = m = []     # assign 'l' and 'm' to empty list
    l.append(42)   # change content of 'l' only
    
    print(l)
    print(m)
    
    a = 100
    b = 0
    [42]
    [42]
    
    • Multple value assignments

      Tuple և List տվյալների տիպերը հանդես են գալիս ոչ միայն որպես տվյալների փաթեթավորման (wrap) յուրօրինակ մեխանիզմ է, այլ նաև մեծ դեր են կատարում շարահյուսական առումով։ Այսպես, օրինակ, նշված տիպերի օգնությամբ կարելի է հայտարարել միանգամից մի քանի փոփոխականներ։

      t = (2, 4)           # assign tuple to 't'
      (x, y) = (5, 10)     # assign elements to variables
      [a, b] = [50, 100]   # assign elements to variables
      
      
      print(t)
      print(x)
      print(y)
      print(a)
      print(b)
      
      # ValueError
      # (a, b) = ('a', 'b', 'c')
      
      (2, 4)
      5
      10
      50
      100
      

      Քանի որ Tuple տիպը որոշ դեպքերում (ոչ ներդրված) կարելի է հայտարարել նաև առանց փակագծերի, ապա փոփոխականների հայտարարման շարահյուսությունն ավելի ընկալելի կարող է դառնալ։

      x, _, y = 7, 10, 50      # middle value is ignored
      n, l = "text", [1,2,3]
      
      
      print(x, y)
      print(n)
      print(l)
      
      7 50
      text
      [1, 2, 3]
      
    • Multiple comparison

      Նույն տրամաբանությամբ կարող են կառուցվել նաև շղթայական համեմատության արտահայտությունները՝ ըստ օպերատորի ասոցիատիվության։ Հետևյալ օրինակում շղթայական համեմատությունը համարժեք է and օպերատորի կիրառման, սակայն համեմատական արտահայտությունն այս դեպքում «արժևորվում» է (evaluate) միայն մեկ անգամ։ Այստեղ, նույնպես, առաջին մասի «սխալի» (False) դեպքում երկրորդ մասը կատարման ենթակա չէ։

      from math import sin, pi
      
      
      if 0 < sin(pi/6) < 0.5:
          print("IEEE 754 makes sin(30°) < 1/2 -", sin(pi/6))
      
      # Same as...
      # x = sin(pi/6)
      # if x >= 0 and x < 100:
      
      IEEE 754 makes sin(30°) < 1/2 - 0.49999999999999994
      
  • Unpacking

    Tuple տիպը տալիս է բազմակի հայտարարման ևս մեկ մեխանիզմ՝ unpacking, որի շնորհիվ կատարվում է «ապափաթեթավորում»։

    def listOfEvens(n1, n2):
        res = [x for x in range(n1, n2) if x % 2 == 0]
        return res
    
    
    x, y, *rest = listOfEvens(6, 24)
    
    
    print(x)
    print(y)
    print(rest)
    
    6
    8
    [10, 12, 14, 16, 18, 20, 22]
    

    Ի տարբերություն ֆունկցիայի արգումենտների նշագրման՝ այստեղ հատուկ նշանով (*) փոփոխականի տեղադիրքը կարող է լինել սկզբում կամ ֆիքսված փոփոխականների միջև։

    *_, last = [x for x in range(24) if x % 2 == 0]
    
    
    print(last)
    
    22
    
    • Argument unpacking

      Աստղանիշ (*) նախածանցով շարահյուսությունը կարելի է կիրառել նաև ֆունկցիայի արգումենտ փոխանցելիս և փոփոխական հայտարարելիս։ Այդ դեպքում ֆուկցիան ստանում է ոչ թե մեկ արգումենտ, այլ տվյալ արգումենտի (list, tuple, etc.) բոլոր էլեմենտները՝ որպես առանձին արգումենտ։

      Այսպես, հավաքածուների էլեմենտները արտաբերելիս, կարելի է այն print ֆունկցիային փոխանցել «ապափաթեթավորված» (unboxed) եղանակով։

      l = [1,2,3,4]
      
      print(*l)
      
      # Same as...
      # print(l[0], l[1], l[2], l[3])
      
      1 2 3 4
      

      Տվյալ շարահյուսությունը կիրառելի է նաև բանալի֊արժեք (key-value) հավաքածուների պարագայում, եթե արգումենտներն անվանական փոխանցման դեպքում համընկնում են։

      def testUnboxing(a, b, c):
          print(a, b, c)
      
      
      d = {'a': 97, 'b': 98, 'c': 99}
      
      testUnboxing(**d)
      
      # Same as...
      # testUnboxing(a=97, b=98, c=99)
      
      97 98 99
      

      Փոփոխական հայտարարելիս նույնպես կարելի է օգտագործել տվյալ շարահյուսությունը։

      a = [*"String"]
      
      # Same as...
      # a = ['S','t','r','i','n','g']
      
      print(a)
      
      ['S', 't', 'r', 'i', 'n', 'g']
      
  • Module entrypoint

    Քանի որ ինտերպրետացիոն լեզուներում մոդուլները (ֆայլերը) ներբեռնելիս ենթակա են կատարման ամբողջությամբ՝ խնդիր է առաջանում առանձնացնել դեկլարատիվ կոդը կատարման ենթակա կոդից և մեկուսացնել կատարման ենթակա կոդը այլ մոդուլներ ներբեռնելու ընթացքում կատարվելուց։ Այդ նպատակով անմիջական կատարման ենթակա կոդը առանձնացվում է հատուկ ձևաչափով պայմանային արտահայտությամբ, որի իսկությունը դրական (True) արդյունք է ունենում միայն այն դեպքում, երբ տվյալ մոդուլը կատարման է ենթարկվում ուղղակիորեն։

    Հետևյալ օրինակում lib.py մոդուլը ներմուծվում է test.py մոդուլ, սակայն անմիջական կատարման արտահայտությունները մեկուսացված են հատուկ միջակայքում և կատարման ենթակա են միայն մոդուլի ուղղակի գործարկման դեպքում, մինչդեռ ազատ հայտարարված ֆունկցիաները ներմուծվում են և հասանելի են։

    # lib.py
    
    # declarations available for import
    def resPrint(res):
        print("Res:", res)
    
    
    # code running only on direct exec of module
    if __name__ == "__main__":
    
        # ... exec some code
    
        print("lib.py: I'm the main module and running directly!")
    
    # test.py
    
    from demo import *
    
    
    if __name__ == "__main__":
    
        # use imported function
        resPrint(42)
    

    Այս դեպքում test.py մոդուլը ուղղակիորեն գործարկելուց հասանելի են ներմուծվող մոդուլի ֆունկցիաները, սակայն մեկուսացված արտահայտությունները չեն կատարվում։

    python test.py
    
    Res: 42
    

    Իսկ lib.py մոդուլի կատարման մասը հասանելի է դառնում միայն տվյալ մոդուլի ուղղակի գործարկման արդյունքում։

    python lib.py
    
    lib.py: I'm the main module and running directly!
    

    Արտաքին միջավայրից փոխանցված արգումենտները հասանելի են sys մոդուլի argv լիստ տիպի փոփոխականի միջոցով, որտեղ առաջին էլեմենտը կատարման ենթակա մոդուլի (ֆայլի) անունն է։

    # test.py
    import sys
    
    
    if __name__ == "__main__":
    
        print(sys.argv)
    
    python test.py argument1 argument2
    
    ['test.py', 'argument1', 'argument2']
    
  • Կրկնություն

    Օբյեկտ֊օրիենտացված ծրագրավորման փուլի վերջում տրվել էր հղում ծանոթության համար: Այդ թեմաների հետ զուգահեռներ անցկացնելու նպատակով տրվում են հավելյալ հղումներ Python լեզվի կոնտեքստում։

    • Հղում ծանոթության համար - Built-in Functions, Decorators, String formatting, GIL, Async/Await

3.11 Օբյեկտ֊օրիենտացման գործիքներ (Python)

[2020-09-25]

Այստեղ կդիտարկվեն միայն Python լեզվի օբյեկտ֊օրիենտացված մոտեցման առանձնահատկությունները, քանի որ բազային հասկացություններն արդեն դիտարկվել են նախորդող դասընթացների ընթացքում։

Օբյեկտ֊օրիենտացման մոտեցումը ինտերպրետացիոն լեզուներում հիմնականում ավելի ազատ է, քան կանոնիկ լեզուներում, առկա են տվյալների փոփոխման ավելի ճկուն մեխանիզմներ, սակայն այդ «ազատության» հետևանքներն ամբողջությամբ մնում են ծրագրավորողի պատասխանատվության տակ։

Կլասերը հայտարարվում են class բանալի֊բառի օգնությամբ, իսկ կլասի անվանումը գրվում է գլխատառ՝ ֆունկցիաների հետ չշփոթելու համար։ Կլասի օրինակը (օբյեկտը) ստեղծվում է կլասի անվանմանը կցելով ֆունկցիայի կիրառման () նշագրումը։

Հետևյալ օրինակում EmptyClass կլասն ունի միայն անվանում և լռելյայն կոնստրուկտոր, որի օգնությամբ ստեղծվում է կլասի օրինակ և կցվում է e փոփոխականին։ Կլասն առանց բովանդակության հայտարարելու համար օգտագործվում է pass օպերատորը, որը ոչ մի կատարում չունի և այս դեպքում դատարկ միջակայք է նշում (միջակայքը փակագծերով առանձնացնող լեզուների պարագայում դրա կարիքը չկա)։

# create an empty class
class EmptyClass:
    pass


# make an instance (object) of EmptyClass
e = EmptyClass()
  • Attributes

    Քանի որ Python լեզվում, ինչպես ինտերպրետացիոն լեզուների մեծամասնության դեպքում, չկան հասանելիության public և private կարգավորիչներ՝ կլասերում հայտարարված բոլոր տվյալները հասանելի են ամբողջությամբ (նաև փոփոխման համար)։ Ավելին՝ կլասի օրինակին (օբյեկտին) կարելի է կցել կլասում չհայտարարված նոր տվյալ։ Հետևյալ օրինակում համատեղվել են նաև տեքստային տվյալների «ֆորմատավորման» (string formatting) տարբեր լուծումներ։

    # create a class with class attributes only
    class TestClass:
        attr1 = "test attribute"
        attr2 = 0
    
    
    # maken an instance
    tc = TestClass()
    
    # old 'printf' string formatting
    print("attr1: %s\nattr2: %s\n" % (tc.attr1, tc.attr2))
    
    # set attributes
    tc.attr1 = "new attribute"
    tc.attr2 = 42
    
    # new string formatting
    print("attr1: {0}\nattr2: {1}\n".format(tc.attr1, tc.attr2))
    
    # set NEW attribute
    tc.attr3 = True
    
    # literal string formatting
    print(f"attr3: {tc.attr3}")
    
    attr1: test attribute
    attr2: 0
    
    attr1: new attribute
    attr2: 42
    
    attr3: True
    

    Ինչպես նշված է մեկնաբանությամբ՝ նման ձևով հայտարարվում են բացառապես կլասի տվյալներ, որոնք հասանելի են նաև կլասի օրինակի համար, ինչը միանշանակ չէ առաջին հայացքից։ Կլասի օրինակի կողմից սեփական համանուն փոփոխական հայտարարելու արդյունքում կլասի տվյալները «ստվերվում» են։

    class TestClass:
        attr = "test attribute"
    
    
    # maken two instances
    tc1 = TestClass()
    tc2 = TestClass()
    
    print(f"tc1.attr: {tc1.attr}")
    print(f"tc2.attr: {tc2.attr}\n")
    
    
    # change class attribute
    TestClass.attr = "CLASS attribute"
    
    print(f"tc1.attr: {tc1.attr}")
    print(f"tc2.attr: {tc2.attr}\n")
    
    
    # create instance attribute
    tc2.attr = "INSTANCE attribute"
    
    print(f"tc1.attr: {tc1.attr}")
    print(f"tc2.attr: {tc2.attr}")
    
    tc1.attr: test attribute
    tc2.attr: test attribute
    
    tc1.attr: CLASS attribute
    tc2.attr: CLASS attribute
    
    tc1.attr: CLASS attribute
    tc2.attr: INSTANCE attribute
    
  • Constructor

    Բացառությամբ կլասի մակարդակում տվյալ ունենալու տրամաբանության՝ վերը նշված օրինակը սովորաբար չի օգտագործվում, քանի որ կատարման վարքագիծը միանշական չէ։ Կլասի օրինակի տվյալները (ատրիբուտները) ընդունված է հայտարարել __init__ կոնստրուկտորի օգնությամբ։

    Ի տարբերություն С լեզվաընտանիքի՝ այստեղ կլասի միջակայքում տվյալների «նախնական» հայտարարում (int age; String name;) չկա, քանի որ այս միջակայքում հայտարարվող բոլոր փոփոխականները պատկանում են կլասին։ Փոխարենը, որպես մշտական առաջին արգումենտ, կոնստրուկտորի ֆունկցիայի համար հայտարարվում է պոտենցյալ ստեղծվող օրինակի (օբյեկտի) հղումը, որն ընդունված է անվանել self: Այն օգտագործվում է բացառապես կլասի հայտարարման միջակայքում և փոխարինում է պոտենցյալ ստեղծվող օրինակի փոփոխականին։ Կլասի օրինակի ստեղծման ժամանակ տվյալ արգումենտը չի փոխանցվում։

    class Ratio:
    
      # constructor with instance as first argument
      def __init__(self, divisor, divident):
    
          # creating instance attributes
          self.divisor = divisor
          self.divident = divident
    
          # create hidden attribute
          self.__num = 42
    
    
    # creating instance without instance argument
    r = Ratio(3,4)
    
    print("Ratio[{0}/{1}]".format(r.divisor, r.divident))
    
    
    # AttributeError
    # print(r.__x)  -  'Ratio' object has no attribute '__x'
    
    
    # hidden attribute is available
    print("Hidden attribute:", r._Ratio__num)
    
    Ratio[3/4]
    Hidden attribute: 42
    

    Տվյալների փոփոխականները տարբեր պատճառներով կարելի է թաքցնել (տես՝ հատուկ նշագրում) ուղղակի հասանելիությունից և տրամադրել համապատասխան setter և getter մեթոդներ, սակայն պաշտպանել հնարավոր չէ։

  • Methods

    Կլասի օրինակի (օբյեկտի) մեթոդները կրկնում են կոնստրուկտորի տրամաբանությունը՝ հայտարարվում են պոտենցյալ օբյեկտի self առաջին արգումենտով, որը մեթոդի կիրառման ժամանակ չի փոխանցվում։

    class Ratio:
    
      def __init__(self, divisor, divident):
          self.divisor = divisor
          self.divident = divident
    
      # all instance methods takes mandatory self
      def decimal(self):
          return self.divisor / self.divident
    
    
    r = Ratio(3, 4)
    
    print(r.decimal())
    
    0.75
    
  • Overloading

    Python լեզվում կան կլասի հատուկ (__<method>__) մեթոդներ, որոնց օգնությամբ կարելի է վերաբեռնավորել տարբեր օպերատորներ և գործողություններ (operator overloading) կլասի օրինակների համար։

    from math import gcd
    
    
    class Ratio:
    
      def __init__(self, divisor, divident):
          gcd_ = gcd(divisor, divident)
          self.divisor = divisor // gcd_
          self.divident = divident // gcd_
    
      # overloading '+' operator
      def __add__(self, other):
    
          if not isinstance(other, self.__class__):
              raise TypeError("operand is not 'Ratio' instance")
    
          newddt = self.divident * other.divident
          newdsr = other.divident * self.divisor + \
                    self.divident * other.divisor
          gcd_ = gcd(newddt, newdsr)
    
          # return new Ratio instance
          return Ratio(newdsr // gcd_, newddt // gcd_)
    
      # overloading '==' operator
      def __eq__(self, other):
    
          if self.__class__ != other.__class__:
              return False
    
          return self.divisor == other.divisor and \
                 self.divident == other.divident
    
      # overloading string representation on print
      def __str__(self):
          return f"Ratio[{self.divisor}/{self.divident}]"
    
    
    r1 = Ratio(6, 8)
    r2 = r1 + Ratio(1, 2)
    
    print(r1)
    print(r2)
    print(Ratio(1, 10) + Ratio(1, 5))     # 0.1 + 0.2 resolved
    print(Ratio(1, 2) == Ratio(3, 6))     # use __eq__ method
    print(Ratio(1, 2) is Ratio(1, 2))     # compares reference
    
    Ratio[3/4]
    Ratio[5/4]
    Ratio[3/10]
    True
    False
    
    • Iterator class

      Հատուկ մեթոդների օգնությամբ կարելի է ստեղծել նաև որոշակի տրամաբանությամբ հետաձգված կատարմամբ ցիկլեր, որոնք ենթակա են for ցիկլերով անցման և հավաքածուների փոխարկման։

      class Ranger:
      
          def __init__(self, end):
              self.start = 0
              self.end = end
      
          def __iter__(self):
              return self
      
          def __next__(self):
              if self.start < self.end:
                  start = self.start
                  self.start += 1
                  return start
              else:
                  raise StopIteration
      
      
      print(list(Ranger(8)))
      
      for n in Ranger(8):
          print(n, end=" ")
      
      [0, 1, 2, 3, 4, 5, 6, 7]
      0 1 2 3 4 5 6 7 
      
  • Inheritance

    Ժառանգականության տեսանկյունից կա մեկից ավելի կլասերից ժառանգելու հավելյալ ճկունություն, որը խորհուրդ չի տրվում չարաշահել, քանի որ այս հնարավորության վարքագիծը նույնպես միանշանակ չէ։

    Ծնողական (parent) կլասերը հայտարարվում են ժառանգորդ կլասը հայտարարելիս՝ հասարակ փակագծերում և բաժանվում են ստորակետով։ Մեկից ավելի կլասերի դեպքում տվյալների և մեթոդների «փնտրումը» տեղի է ունենում ըստ հայտարարման՝ ձախից աջ։

    Հետևյալ օրինակում D կլասը հայտարարվում է երկու ծնողական կլասի ժառանգականությամբ՝ D(B, C): Կլասի d փոփոխականին կցված օրինակով կիրառվում են show և show_x մեթոդները։ Քանի որ D կլասը չունի show մեթոդ՝ կլասի օրինակը մեթոդը փնտրում է հաջորդաբար B և C կլասերում։ B կլասում մեթոդը չհայտնաբերելուց հետո միանգամից անցում է կատարվում հաջորդ ժառանկագան մակարդակ (B -> Ashow_x մեթոդը գտնվում է միայն C կլասում, սակայն այս մեթոդում հղում ունեցող self.x փոփոխական կլասում հայտարարված չէ։ Քանի որ տվյալի փնտրումը տեղի է ունենում սկզբնական ժառանգականության հաջորդականությամբ՝ այն վերցվում է A կլասից։

    class A:
        def __init__(self):
            self.x = 42
    
        def show(self):
            print("Class A")
    
    
    class B(A):
        pass
    
    
    class C:
        def show(self):
            print("Class C")
    
        def show_x(self):
            print("Class C:", self.x)
    
    
    class D(B, C):
        pass
    
    
    d = D()
    
    d.show()
    d.show_x()
    
    Class A
    Class C: 42
    

    Վերը նշված կլասերի ժառանգականությունը կարելի է գրաֆիկորեն պատկերել հետևյալ կերպ՝

       A [x, show]
      /
     /
    B     C [show, show_x]
     \   /
      \ /
       D
    
  • Պրակտիկ օրինակ

    Նպատակն է ստեղծել բազմապլատֆորմ (cross-platform) գրաֆիկական ինտերֆեյսով (GUI) ցուցադրական հավելված՝ օգտագործելով Qt հայտնի համակարգը Python ծրագրավորման լեզվին փոխարկող PyQt ծրագրադարանը։ Ծրագրադարանի ներբեռնումը և միջավայրի կարգավորումը կազմակերպվում է pipenv հավելվածի օգնությամբ՝ ըստ այս պրոյեկտին կից Pipfile ֆայլի բովանդակության:

    Միջավայրի կարգավորման համար հարկավոր է, նախ, pip install pipenv հրամանի օգնությամբ տեղադրել pipenv հավելվածը (pip հավելվածն իր հերթին տեղադրվում է Python ծրագրավորման փաթեթի հետ միասին, տես՝ միջավայրի կարգավորում)։ Այնուհետև պրոյեկտի հասցեում հարկավոր է կիրառել pipenv install հրամանը, որը կտեղադրի Pipfile ֆայլում նշված ծրագրադարանները։ Տեղադրումն ավարտելուց հետո կարելի է կատարել հավելվածի միակ ֆայլը pipenv run python main.py հրամանի օգնությամբ։

    Որոշ ինտեգրված ծրագրավորման միջավայրեր (օրինակ՝ PyCharm) թույլ են տալիս աշխատել վիրտուալ միջավայրերի կարգավորման հավելվածների հետ։

    Հավելվածի կոդին առցանց կարելի է ծանոթանալ հետևյալ հասցեով՝ GitHub։
    Հավելվածը կարելի է նաև «կրկնօրինակել» (git clone) համապատասխան գործիքներով կամ ներբեռնել որպես դասընթացների ընդհանուր պրոյեկտ (project)` GitHub:


3.12 Ֆունկցիոնալ մոտեցում (Lisp)

[2020-11-27]

Ֆունկցիոնալ ծրագրավորման առանձնահատկությունը կայանում է մաթեմատիկական տեսանկյունից ֆունկցիաների արժեքների հաշվարկման մեջ՝ օգտագործելով այլ ֆունկցիաների հաշվարկման արդյունքները, ինչը չի ենթադրում «վիճակի» (state) ուղղակի պահպանում։ Ի տարբերություն պրոցեդուրալ ծրագրավորման, որը հաջորդական կատարմամբ փոփոխում է ծրագրի «վիճակը»։

Վերը նշված համատեքստում միմյանց համապատասխանաբար հակադրվում են նաև դեկլարատիվ և իմպերատիվ մոտեցումները։

Ֆունկցիոնալ ծրագրավորման հիմնական առանձնահատկություններն են առաջին դասի ֆունկցիաները (first-class functions) և հղման թափանցիկությունը (referential transparency

  • Lisp

    Ֆունկցիոնալ ծրագրավորման կիրառական օրինակներից է Lisp (LISP - LISt Processor) ծրագրավորման լեզուն, որը «բարձր մակարդակի» ամենահին լեզուներից է (1958թ․)։

    Բացի դինամիկ տիպիզացիայից՝ այս լեզուն հատկանշական է իր յուրօրինակ շարահյուսությամբ և պարզունակությամբ։ Որպես առանձնահատկություն՝ հատկանշական է նաև լեզվական «համասեռությունը» (homoiconicity), որը թույլ է տալիս կատարման ենթակա կոդը և տվյալները արտահայտել միևնույն շարահյուսությամբ։ Այսպիսով ամբողջ լեզվական շարահյուսությունը կարելի է բնութագրել, օրինակ, հետևյալ արտահայտությամբ`

    (func arg1 arg2 arg3)   ;; 3 args passed to function
    

    ․․․որտեղ փակագծերը համարժեք են արտահայտության որոշակի կատարման, առաջին սիմվոլը (symbol) կատարման ենթակա ֆունկցիան է, իսկ հաջորդող սիմվոլները ֆունկցիային փոխանցվող արգումենտներն են, որոնք կարող են լինել ներդրված արտահայտություններ։

    Վերը նշված օրինակի համարժեքը C լեզվաընտանիքի շարահյուսությամբ կարող է լինել func(arg1, arg2, arg3) արտահայտությունը։
    Համարժեք օրինակներ են նաև` (f) - f(), (f (g x)) - f(g(x)) արտահայտությունները։

    Ի տարբերություն այլ լեզուների, այս նշագրումն օգտագործվում է բառացիորեն բոլոր արտահայտությունների համար։

    ;; Polish (prefix) notation
    
    (+ 2 5)                    ;; => 7
    
    (- (+ 2 3 4) 2)            ;; => 7
    
    (> 7 2)                    ;; => T (True value)
    
    (if (> 7 2) "True" "Nil")  ;; => True
    
    (let ((p (> 7 2))
          (t "True")
          (f "False"))
      (if p t f))              ;; => True
    

    Նույն շարահյուսությամբ են կազմվում տվյալները։

    (list 1 2 3 4 5)           ;; => '(1 2 3 4 5)
    
    '(1 2 3 4 5)               ;; => '(1 2 3 4 5) 
    
    (1 2 3 4 5)                ;; => Error: 1 is not a function
    
    (car '(1 2 3 4 5))         ;; => 1
    
    (cdr '(1 2 3 4 5))         ;; => '(2 3 4 5)
    

    Այսպիսի շարահյուսության բացասական կողմերից հաճախ նշում են փակագծերի ընթեռնելիությունը, հատկապես փակվող փակագծերի հաջորդականության պարագայում։ Սակայն կան նաև ակնհայտ առավելություններ, ինչպիսիք են միջակայքի (scope) նշագրումը և կատարման սեմանտիկ հաջորդականությունը, որոնք արտահայտվում են միակ սահմանազատող նշանի՝ փակագծերի, և համապատասխան ներդրվածության օգնությամբ՝ բացառելով տարընթերցումը։

    Շարահյուսական համասեռության շնորհիվ կոդը կարող է գեներացնել մեկ այլ կոդ։ Նման հատկություն ունեցող ֆունկցիաները կոչվում են մակրոսներ (macros plur.)։ Մակրոսների օգնությամբ հայտարարվում են այնպիսի արտահայտություններ, որոնք այլ լեզվական շարահյուսություններում կոչվում են «հատուկ սիմվոլներ» (special symbols) կամ «բանալի֊բառեր» (keywords)։ Դրանցից են, օրինակ, փոփոխականի կամ ֆունկցիայի հայտարարման մակրոս֊ֆունկցիաները։

    ;; define variable
    (defvar *x* 42)       ;; * - idicates global
    
    ;; define function
    (defun func (a1 a2 a3)
       (if (= a1 *x*)
           ("forty two")                   ;; if true  - then
           (write-to-string (+ a2 a3)))))  ;; if false - else
    
    (func 42 10 2)        ;; => "forty two"
    
    (func (-5) 10 2)      ;; => "12"
    
    
    (defun print-hello ()
      (write "Hello!"))   ;; writes string to stdout, returns Nil
    
    (print-hello)         ;; => Nil
    

    Նմանօրինակ շարահյուսությունը հատուկ է AST (abstract syntax tree) կառուցվածքին, որը արտահայտությունների իմաստաբանական հաջորդականության արտահայտման ընդունված ձև է գրեթե բոլոր լեզունների համար, այդ թվում՝ բնական։

  • Clojure

    Lisp լեզվի դիալեկտներից առանձին դիտարկման է արժանի Clojure լեզուն, որը որպես կատարման միջավայր օգտագործում է JVM վիրտուալ մեքենան և ապահովում է Java ծրագրադարանների (libraries) հետ փոխգործելիություն։

    Որպես բազային տիպեր այստեղ լռելյայն վերցվում են Java ծրագրավորման լեզվից, որից ուղղակի կերպով կարելի է «ներմուծել» (import) ցանկացած այլ կլաս կամ մոդուլ։

    (type 42)         ;; => java.lang.Long
    
    (type 1.618)      ;; => java.lang.Double
    
    (type \a)         ;; => java.lang.Character
    
    (type "Text")     ;; => java.lang.String
    
    (type true)       ;; => java.lang.Boolean
    

    Զանգվածների և հավաքածուների պարագայում մոտեցումն այլ է։ Այստեղ կիրառվում են հատուկ նախագծված ընդլայնված տիպեր, որոնք ավելի կայուն են՝ persistent (փոփոխման դեպքում բնօրինակը մնում է նույնը, ստեղծվում է կրկնօրինակ), և, միաժամանակ, ռեսուրսների օգտագործման տեսանկյունից՝ օպտիմալ (փոփոխման արդյունքում միևնույն ծավալի կրկնօրինակ ստեղծելու փոխարեն կրկօրինակվում է միայն փոփոխվող մասը՝ պահպանելով բնօրինակի հղումը)։

    (type '(1 2 3))   ;; => clojure.lang.PersistentList
    
    (type [1 2 3])    ;; => clojure.lang.PersistentVector
    
    (type #{1 2 3})   ;; => clojure.lang.PersistentHashSet
    
    
    ;; comma-separated notation is allowed
    (type '(1,2,3))
    

    Lisp լեզվաընտանիքի մեկ այլ առանձնահատկություններից է «բանալի֊բառերի» տրամաբանությունը։ Այստեղ բանալի բառը փոխարինում է այլ լեզուներում կիրառվող տեքստային տվյալի, որն օգտագործվում է ծրագրային մանիպուլյացիաների համատեքստում։

    (type :abc)           ;; => clojure.lang.Keyword
    
    (type {:a 97 :b 98})  ;; => clojure.lang.PersistentArrayMap
    
    
    ;; defining persistent hash-map
    (def price {:base 180 :sale 165})
    
    (:base price)         ;; => 180
    (:sale price)         ;; => 165
    

    Փոփոխման ենթակա տվյալներ հայտարարելու դեպքում պետք է այն ուղղակիորեն նշել՝ փոխանցելով բուն տվյալները atom ֆունկցիային։

    ;; defining shared synchronous variable 'n'
    (def n (atom 2))
    
    (type n)              ;; => clojure.lang.Atom
    (type @n)             ;; => clojure.lang.Long
    
    (swap! n inc)         ;; => increment n
    
    (println @n)          ;; 3
    
    • Functions

      Որպես ֆունկցիոնալ լեզու՝ այստեղ կան ոչ միայն անվանական ֆունկցիաների այլ նաև անանուն ֆունկցիաների հայտարարաման ճկուն հնարավորություններ։

      ;; ordinary function declaration
      (defn square
        [n]
        (* n n))
      
      
      ;; in-place anonymous function usage
      ((fn [x] (x + 1)) 6)           ;; => 7
      
      ;; shorter notation (% - arg placement)
      (#(+ % 1) 6)                   ;; => 7
      
      (#(+ %1 %2) 5 37)              ;; => 42
      

      Ֆունկցիաների հայտարարման դեպքում նույնպես կան ընդլայնումներ։

      ;; in-place overloading
      (defn greet
        ([]
         (println "Hello!"))
        ([name]
         (println (format "Hello %s!" name))))
      
      
      (defn string->integers         ;; special symbols in name
        [^String text]               ;; argument type notation
        (let [lst (atom [])]         ;; local value definition
          (doseq [c text]            ;; sequence for each element
            (swap! lst #(conj % (int c))))
          lst))
      
      
      ;; calling functions (see results below)
      (greet)
      
      (greet "Clojure")
      
      (string->integers "abcde")
      
      Hello!
      Hello Clojure!
      [97,98,99,100,101]
      
    • Interoperability

      Java բազային ծրագրադարաններից կարելի օգտվել առանց վերջինների ուղղակի ներմուծման։

      ;; uasge of static method floor of Math class
      (Math/floor 3.9)               ;; => 3.0
      
      
      ;; get system locale from Java module
      (java.util.Locale/getDefault)  ;; => en_US
      
      
      ;; call method of String - "text".toUpperCase()
      (.toUpperCase "text")          ;; => TEXT
      
    • Laziness

      Ֆունկցիոնալ լեզուներում ընդունված մեխանիզմ է «ծույլ» կատարումը (lazy evaluation), որը Java միջավայրում կիրառվում է Stream API օգնությամբ (Java 8 տարբերակից սկսած), իսկ Python միջավայրում առկա է հատուկ ֆունկցիաների պարագայում միայն (range, map, zip, etc.)։

      Այսպես, օրինակ, հետևյալ ֆունկցիոնալ կոմպոզիցիայում 97-65535 միջակայքի ամբողջ թվերով հավաքածու չի ձևավորվում։ Փոխարենը՝ range, filter և map ֆունկցիաները հետաձգում են իրենց կատարումը և հաջորդաբար գեներացնում են վերադարձվող արժեքները ըստ պահանջի, տվյալ դեպքում՝ take ֆունկցիայի առաջին արգումենտին համապատասխան։

      (take 5 (map #(char %) (filter odd? (range 97 (Math/pow 256 2)))))
      
      (\a \c \e \g \i)
      

      «Ծույլ» ֆունկցիաները կազմվում են նաև ռեկուրսիվ կոնստրուկցիաների օգնությամբ, որոնք ավելի մանրամասն դիտարկվում են հաջորդող երկու դասընթացների ընթացքում։


3.13 «Զուտ» ֆունկցիոնալ մոտեցում (Haskell)

[2020-11-30]

  • TODO

3.14 Ալգորիթմներ (պրակտիկ)

[2020-12-02]

  • TODO

3.15 Տվյալների Բազա

[2020-12-04]

  • TODO

3.16 Ցանցային հաղորդակցություն

[2020-12-07]

Խորհուրդ է տրվում վերհիշել առաջին փուլի Ցանց (Network) ներկայացումը։

Ցանցային հաղորդակցությունը ենթադրում է մեկից ավելի բաղադրիչների միջև տեղեկատվության փոխանցում։ Տվյալ վերացականության մեջ ենթադրվում է, նվազագույնը, մեկ սերվեր (server), ինչը ծառայում է որպես տեղեկատվության «մատուցող» կողմ, և մեկ կլիենտ (client), ինչը ենթադրում է տեղեկատվության որոշակի հարցում և համարվում է տեղեկատվության սպառող կողմ։ Մեկից ավելի սերվերների պարագայում դրանք կարող են ծառայել նաև որպես կլիենտ և հարցում կատարել մեկ այլ սերվերի՝ գտնվելով «միջանկյալ» դերում։

Հարկ է նշել մեկ անգամ ևս, որ ցանցային հաղորդակցության վերացականությունը կարող է ծավալվել մեկ սարքավորման լոկալ շրջանակներում ծրագրային տարբեր բաղադրիչների միջև։

  • Server

    Տվյալ տերմինաբանությունը չունի որոշակիություն, և առանց հստակեցման դժվար է պատկերացնել քննարկման առարկան. կան պատկերացումներ ինչպես սարքավորման, այնպես էլ ծրագրային ապահովման համատեքստում։ Մինչդեռ սերվեր սարքավորումը ոչ այլ ինչ է, քան հատկացված (հիմնականում՝ հատուկ պարամետրերով) համակարգիչ, որում աշխատում է որոշակի տեղեկատվություն «մատուցող» ծրագրային հավելված։

    Այսպես, սարքավորումը, որի հիմնական ֆունկցիոնալը առանձին ֆայլերի տրամադրման սերվերն է, նույնպես դիտարկվում է որպես ֆայլերի սերվեր։ Երկու դեպքում էլ գործ ունենք File server հասկացողության հետ, մի դեպքում՝ որպես հավելված (software), մյուս դեպքում՝ որպես սարքավորում (hardware)։ Միևնույն սարքավորման սահմաններում տարբեր սերվերային հավելվածների առկայության պարագայում՝ սարքավորման նշանակությունը ընդհանրացվում է։ Որոշ դեպքերում, գործող սերվերային հավելվածի պարագայում, սարքավորումը դիտարկվում է առաջնային ֆունկցիոնալի համատեքստում, ինչպես օրինակ կենցաղային ուղորդիչների (router) դեպքում, որոնք գրեթե առանց բացառության ունեն DHCP սերվեր հավելված։ Կորպորատիվ և ավելի մեծ մասշտաբներում սարքավորումները խմբավորվում են որպես մեկ ընդհանուր սերվեր՝ սերվերային սարքավորումների բլոկ:

    DHCP սերվեր֊հավելվածի նպատակն է լոկալ ցանցին միացած սարքավորմանը, ի պատասխան համապատասխան հարցման, տրամադրել դինամիկ հասցե և գրանցել այն հասցեների սեփական աղյուսակում, եթե արդեն գրանցված չէ։ Օրինակ՝ ամեն անգամ սարքավորումը Wifi ցանցին կապակցելու պրոցեսում։

    Սերվերների տարբերակումը կատարվում է ըստ տեղեկատվության հարցման և տրամադրման օգտագործվող «համաձայնագրերի» (պրոտոկոլ)՝ HTTP Server, FTP Server, DHCP Server, POP Server, SMTP Server․․․ կամ ըստ նպատակային նշանակության՝ Web Server, File Server, Media Server, Proxy Server․․․ Վերջիններն իրենցից ներկայացնում են հաճախ մեկից ավելի սերվեր֊հավելվածների կոմպոզիցիա։

  • Client-Server

    Կլիենտ֊սերվեր փոխհաղորդակցության ամենատարածված տարբերակն է միջին վիճակագրական ինտերնետ֊օգտատիրոջ և որևէ վեբկայքի փոխհամագործակցությունը, որը հիմնականում տեղի է ունենում HTTP պրոտոկոլի օգնությամբ։ Սերվերային մասն այս դեպքում ընդհանրացվում է որպես Web Server:

    • HTTP (HyperText Transfer Protocol)

      HTTP պրոտոկոլը որպես տրանսպորտային պրոտոկոլ է օգտագործում TCP պրոտոկոլը։ Ըստ այս պրոտոկոլի, ինչպես և հաճախ այլ պրոտոկոլների պարագայում, փոխանցվող տեղեկատվության բայթերի հաջորդականությունն ունի «գլխամաս» (header), որի մաս է կազմում հարցման (request) մեթոդը և վերադարձի (response) կոդը, և բուն տեղեկատվության «օգտակար բեռը» (payload)։

      HTTP պրոտոկոլի միջոցով փոխհաղորդակցության ընդունված մոդելներից է REST (Representational state transfer) մոդելը, որը ունի հստակ սահմանափակումներ և պայմաններ։ Այս մոդելին համապատասխանող վեբ֊«ծառայությունները» (web services) կոչվում են RESTful։ HTTP հարցումներն, այսպիսով, հաճախ անվանում են նաև REST հարցումներ։ HTTP պրոտոկոլը համարվում է stateless, քանի որ կոնկրետ կապի կոնտեքստում «վիճակ» (state) չի պահպանում։ HTTP 1.1 տարբերակից սկսած հաստատվող կապը կարելի է նշել «պահպանվող» (Connection: keep-alive)` TCP պրոտոկոլի մակարդակում գործող կապը պահպանելու և վերաօգտագործելու նպատակով։

      Այսօր վեբ ռեսուրսներից օգտվելու համար ավելի հաճախ օգտագործվում է HTTPS (HyperText Transfer Protocol Secure) պրոտոկոլը, որը HTTP պրոտոկոլի ընդլայնված տարբերակն է և օգտագործում է TLS (Transport Layer Security) կամ ավելի հին SSL (Secure Socket Layer) պրոտոկոլները՝ տեղեկատվության գաղտնագրման (encryption) և գաղտնազերծման (decryption) համար։

      HTTP 1.0 պրոտոկոլն իսկզբանե ունեցել է 3 մեթոդ՝ GET, HEAD, POST, որոնք 1.1 տարբերակում լրացվել են ևս 6 մեթոդով՝ PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH:

      HTTP հարցումների գերակշռող մեծամասնությունն այսօր կատարվում է վեբ֊բրոուզերի (Chrome, Firefox, և այլ) օգնությամբ, սակայն հարցումներ կարելի է կատարել նաև ժամանակակից գրեթե բոլոր ծրագրավորման լեզուների օգնությամբ, որոնք ունեն ՕՀ ռեսուրսներից օգտվելու հնարավորություն։ Հարցումը կատարվում է ըստ հասցեի և մեթոդի։

      Հետևյալ հրամանը curl հավելվածի օգնությամբ կատարում է GET հարցում հttps://docs.ingenium.am/counter.html հասցեով առանց հավելյալ պարամետրերի (-i արգումենտը վերադարձը արտաբերում է ամբողջությամբ՝ ներառելով «գլխամասը»)։

      curl -i https://docs.ingenium.am/counter.html
      
      HTTP/2 200 
      content-type: text/html
      content-length: 645
      last-modified: Sun, 26 Jul 2020 19:55:54 GMT
      accept-ranges: bytes
      server: AmazonS3
      date: Wed, 09 Dec 2020 09:32:07 GMT
      etag: "3b45c75f004de6ad2c5f1d41a219bde0"
      x-cache: RefreshHit from cloudfront
      via: 1.1 a492e3d7e1e07970b5b6e383c833d8a0.cloudfront.net (CloudFront)
      x-amz-cf-pop: SOF50-C1
      x-amz-cf-id: tc616iLt4wU-dycGZzHCR2CH5LlT8RtmNMrDrUKXUqmRmjACtgPYoQ==
      
      <?xml version="1.0" encoding="utf-8"?>
      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
      <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
        <head>
          <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          <title></title>
          <meta name="author" content="Levon Melikyan" />
      
          <link rel="stylesheet" href="./counter/style.css" type="text/css" media="screen" />
          <script type="text/javascript" src="./counter/counter.js"></script>
        </head>
        <body>
        </body>
      </html>
      

      Հարցման վերադարձը (response of the request), որը պատկերված է վերևում, բաղկացած է երկու հատվածից։ Վերին հատվածը գլխամասն է (header), որով նշում են ստորին հատվածի` բուն տեղեկատվության (payload) չափորոշիչները և այլ մետատվյալներ։ Նույն հարցումը բրոուզերի օգնությամբ կատարելիս, այսինքն՝ համապատասխան դաշտում հասցեն մուտք անելով, վերը պատկերված տեղեկատվությունը հասանելի է դառնում բրոուզերին, որը, վերծանելով մետատվյալները և բուն տեղեկատվությունը, փորձում է արտաբերել այն համաձայն տեղեկատվության ձևաչափի՝ HTML, PDF, JPG, և այլ։

      Հարցման պատասխանի մետատվյալների մեջ առաջնային դեր ունի վերադարձի կոդը - HTTP status code։ Հաճախ հանդիպող կոդերն են՝

      200 OK 2xx - Success
      304 Not Modified 3xx - Redirection
      400 Bad Request 4xx - Client Error
      403 Forbidden  
      404 Not Found  
      500 Internal Server Error 5xx - Server Error
      502 Bad Gateway  
      503 Service Unavailable  

      Միջին վիճակագրական ինտերնետ֊օգտատիրոջը այս ցուցակից ծանոթ է 404 կոդը, որը հաճախ արտաբերվում է բրոուզում, երբ հարցվող նյութը սերվերում չի գտնվում։

      HTTP հարցումներ կատարելու համար կարելի է օգտագործել հատուկ հավելվածներ, ինչպիսիք են, օրինակ, Telnet տեքստային տերմինալի հավելվածը, կամ այլ գրաֆիկական/վեբ միջերեսով հավելվածներ (RESTED, POSTMAN, https://apitester.com/

    • Static server (HTTP)

      Վեբկայքերի սկզբնական շրջանում վեբ֊սերվերները տրամադրում էին սեփական պահոցներում տեղադրված ֆայլերը՝ հասցեների այն տրամաբանությամբ, որով կազմակերպված էր պահոցի ներքին դասավորվածությունը։

      Ցուցադրական ստատիկ վեբ֊սերվեր հեշտությամբ կարելի է գործարկել սեփական համակարգչում օգտագործելով Python ծրագրավորման միջավայրի համապատասխան մոդուլը։ Ստեղծեք ./web-static նոր հասցե (directory) հետևյալ պարունակությամբ՝

      .
      ├── img
      │   └── example.jpg
      └── index.html
      

      որտեղ example.jpg ֆայլը կարող եք ընտրել ըստ ցանկության, իսկ index.html ֆայլի բովանդակությունը հետևյալն է՝

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Test HTML</title>
      </head>
      <body>
          <h2>Test Title</h2>
          <p>Test text paragraph.</p>
          <img src="img/example.jpg"/>
      </body>
      </html>
      

      Նոր ստեղծված ./web-static հասցեով Python ինտերպրետատորի օգնությամբ գործարկեք http.server մոդուլը։

      python -m http.server
      

      Տերմինալում կհայտնվեն հատկացված պորտի (port) հաջորդող հարցումների մասին տեղեկատվություն։ Տվյալ դեպքում նշվում է 0.0.0.0 ներքին loopback IP հասցեն։ Սակայն սերվերը հասանելի է նաև 127.0.0.1 և localhost ներքին հասցեներով, որոնք ընդունված են որպես ներքին IP հասցեավորում։

      Այսպիսով, օրինակ, 8000 պորտը նշվելուց հետո, բրոուզերի օգնությամբ սերվերին կարելի է դիմել localhost:8000 հասցեով։ Առանց կոնկրետ ֆայլի հասցեն նշելու՝ լռելյայն հղում է արվում index.html ֆայլի համար։ Պահանջվող տեղեկատվությունը ստանալուց հետո (տվյալ օրինակում՝ index.html) բրոուզերը վերծանում է բովանդակությունը, և, ըստ պահանջի, հավելյալ հարցումներ է կատարում նշված հղումների օգնությամբ ներդրված ֆայլերը ստանալու (ներբեռնելու) համար։ Այս օրինակում կա մեկ հարաբերական հղում img/example.jpg, որի ամբողջական հասցեն ստացվում է կրող ֆայլի (index.html) նկատմամբ՝ localhost:8000/img/example.jpg ամբողջական տեսքով։

      curl --head http://localhost:8000/img/example.jpg
      
      HTTP/1.0 200 OK
      Server: SimpleHTTP/0.6 Python/3.9.0
      Date: Wed, 09 Dec 2020 10:52:26 GMT
      Content-type: image/jpeg
      Content-Length: 11710
      Last-Modified: Wed, 09 Dec 2020 10:18:10 GMT
      

      Վերը նշված հրամանով curl հավելվածը --head արգումենտի օգնությամբ արտաբերում է միայն գլխամասը, քանի որ բովանդակությունը տեքստային տերմինալում արտաբերման ենթակա չէ։

      Հարկավոր է ուշադրություն դարձնել, թե որ պորտին են կցված սերվերի հարցումները, քանի, որ տվյալ օրինակում մոդուլ֊հավելվածը որոշակի տրամաբանությամբ է ընտրում այն։ Ցանկության դեպքում հրամանը կարելի է հավելել՝ նշելով կոնկրետ պորտ, օրինակ՝ python -m http.server 8442, եթե ՕՀ կողմից այն չմերժվի արդեն հատկացված լինելու պատճառով։


3.17 Web ծրագրավորման բաղադրիչներ

[2020-12-09]

Ի տարբերություն ստատիկ վեբ-կայքերի, որոնք գործում են ստատիկ տվյալներ տրամադրող սերվերի հիմքում, ժամանակակից վեբ-կայքերի գերակշռող մեծամասնությունը ենթադրում են անհատական սեսիա (session) և դինամիկ գեներացվող էջեր՝ ըստ համապատասխան հարցման և օգտատիրոջ։

Նման ծառայությունների նույնիսկ նվազագույն պահանջները, օրինակ՝ օգտատիրոջ սեսիա պահպանելը, HTTP սերվերի պատասխանատվությունից դուրս են (քանի որ, ինչպես նախորդ դասընթացի ընթացքում է նշվել, HTTP պրոտոկոլը չի պահպանում կապի մասին որևէ տեղեկատվություն և համարվում է stateless)։ Այդ պատճառով նախորդող հարցումների համատեքստում օգտատիրոջը նույնականացնելու և վերջինին հատուկ տեղեկատվություն գեներացնելու ֆունկցիան փոխանցվում է HTTP սերվերի հետ միևնույն օպերացիոն համակարգում կամ հեռավար գործող համապատասխան հավելվածին, որն անվանում են վեբ֊հավելված՝ Web Application։

Եթե նախկինում այդպիսի հավելվածները, օրինակ` CGIs (Common Gateway Interface scripts), ունեին նվազագույն ֆունկցիոնալ, այն է՝ պատասխան տեղեկատվության մեջ «ներարկել» ընթացիկ հարցման մասին տվյալներ, և հենց այդ պատճառով երկար ժամանակ կոչվում էին «սկրիպտ», ապա ժամանակակից վեբ֊հավելվածները ավտոմատացման օժանդակ գործիքներով ամբողջական ենթակառուցվածքներ են և ծախսում են հաշվարկային լուրջ ռեսուրսներ։ «Մնայուն» (persistent) տվյալները՝ օգտատերերի հաշիվներ, լոգեր, և այլ, միաժամանակ ենթակա են պահպանման տվյալների բազայում, որն առանձին սերվերային հավելված (միգուցե նաև սարքավորում) է ենթադրում։ Տվյալների բազայի կառավարման համակարգի հետ համագործակցության ծրագրային մասը, որպես կանոն, նույնպես վեբ֊հավելվածի մաս է կազմում։

Վեբ֊հավելվածի ծավալին ուղիղ համեմատական կարիք է առաջանում ծրագրային առումով «սպասարկել» նաև պոտենցյալ կլիենտ֊հավելվածը, որը միտված է օգնելու հավելյալ ֆունկցիոնալով և ենթակա է գործարկման կլիենտի կողմում (օգտվողի սարքավորմամբ)՝ client-side։ Այս հավելվածը նույնպես մաս է կազմում մեկ ընդհանուր վեբ֊հավելվածի, սակայն ֆունկցիոնալ տեսանկյունից առանձնացվում է որպես Front-end (դիմային պլանում գործող), ի հակադրություն սերվերային մասի, որը կոչվում է Back-end (ետին պլանում գործող)։

Վեբ֊հավելվածը կարող է չընդգրկել կլիենտին հատուկ հավելված (Front-end) և մատակարարել բացառապես հարցմանը համապատասխան սերիալիզացված տվյալներ (XML, JSON, և այլ․)։ Այդպիսի հավելվածները կոչվում են RESTful Web services և նախագծվում են REST (REpresentational State Transfer) ճարտարապետության սկզբունքներով։ Տրամադրելով սերիալիզացված տվյալներ՝ այս տեսակի վեբ֊հավելվածները առանց համապատասխանեցման կարող են ծառայել ցանկացած հավելվածի։

Կան բազմապիսի անվճար և վճարովի REST API ծառայություններ, որոնք պատրաստ են տրամադրել տարբեր բնույթի, որակի, և քանակի տվյալներ՝ պահպանման, վերլուծության և արտաբերման ենթակա։ Նման վեբ֊ծառայությունները (Google, Amazon, Facebook, Instagram, և այլ) հաճախ տրամադրում են տարբեր ծրագրավորման լեզուների ծրագրադարաններ՝ նախագծվող համակարգերի ավտոմատացման համար։

  • Back-end

    Ժամանակակից գրեթե բոլոր ծրագրավորման լեզուներում կան ցանցային համակարգերի հետ աշխատանքի համապատասխան ծրագրադարաններ (libraries), որոնք ենթադրում են տարբեր պրոտոկոլների և սերվերների հետ աշխատանքի հավելյալ վերացականություն՝ թաքցնելով ավտոմատացման ավանդական լուծումները։ Այս ենթաոլորտում համեմատաբար ավելի մեծ փորձ ունեն և համեմատաբար ավելի մեծ հեղինակություն են վայելում դինամիկ տիպիզացմամբ ինտերպրետացիոն միջավայրով այնպիսի լեզուներ, ինչպիսիք են, օրինակ, PHP և Python լեզուները, ինչպես նաև՝ ստատիկ տիպիզացմամբ բիզնես֊սպեցիֆիկացիաներին (օր․՝ Enterprise) համապատասխանող լեզուներ, ինչպիսիք են Java և C# լեզուները։ Այստեղ մեծ դեր են կատարում ինչպես լեզվական հնարավորությունները, այնպես էլ ժամանակով փորձված ծրագրադարանները (libraries) և «հենքերը» (frameworks)։

    • Back-end ծրագրավորման լեզուների շարքում ներկայումս հայտնի է նաև JavaScript (JS) լեզուն, սակայն այս ենթաբաժնում միտումնավոր բաց է թողնված և հիշատակվում է հաջորդ ենթաբաժնում (տես՝ JavaScript
    • Հիշատակվող ծրագրավորման լեզուները ակտուալ են այս դասընթացի սեղմագրի հրապարակման ժամանակահատվածի համար։

    Վեբ֊հավելվալվածի սերվերային մասի առաջնային գործառույթը՝ տարբեր պրոտոկոլներով ներկայացված հարցումներին համապատասխան տվյալների գեներացումն է և, ըստ անհրաժեշտության, այդ հարցումներին պատասխան տրամադրելը։ HTTP պրոտոկոլով տրամադրվող պատասխանները հիմնականում HTML (HyperText Markup Language) տեքստային ֆայլեր են, որոնցում համակարգվում է հարցմանը համապատասխան էջի տեքստային և գրաֆիկական ձևավորումը, ինչպես նաև՝ սահմանվում են օժանդակ «գործիքներ» (տես՝ Front-end

    • Routing (ՈՒղղորդում)

      Այս մեխանիզմը ծառայում է հասցեական հարցումների «ուղղարդմանը»։ Տարբեր «հենքերի» (frameworks) միջավայրում այն սահմանվում է նախապես ընդունված կարգի համաձայն՝ ընդունված ձևաչափով։

      Այսպես, օրինակ, Java Servlet API ընդունված ձևաչափն է հասցեական ուղորդումը կարգավորել web.xml ֆայլում, որը կոչվում է servlet mapping և գտնվում է վեբ֊հավելվածի հիմնական հասցեի նկատմամբ ․/WEB-INF հասցեով։
      Հետևյալ օրինակում /game և /game/* (ցանկացած վերջածանցով) հասցեական հարցումները փոխանցվելու (ուղորդվելու) են server.servlets.GamaServlet կլասին։

      <!-- Example of a part of servlet mapping -->
      ...
      <servlet>
          <servlet-name>Game</servlet-name>
          <servlet-class>server.servlets.GameServlet</servlet-class>
      </servlet>
      <servlet-mapping>
          <servlet-name>Game</servlet-name>
          <url-pattern>/game</url-pattern>
          <url-pattern>/game/*</url-pattern>
      </servlet-mapping>
      ...
      

      Պատասխանատու ֆունկցիան կամ մեթոդը իր հերթին պետք է կարգավորված լինի համապատասխան հարցումների համար։ Տվյալ դեպքում՝ սերվլետը պետք է ժառանգի HttpServlet կլասից և ըստ պահանջի իրականացնի ծնողական կլասի մեթոդները։

      // imports...
      
      public class GameServlet extends HttpServlet {
      
          @Override
          public void doGet(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
      
              // ... e.g.
      
              // if (request.getParameter("gameHex") == null) {
              //     response.sendRedirect("/lobby");
              // }
          }
      
          @Override
          protected void doPost(HttpServletRequest request, HttpServletResponse response)
              throws IOException {
              // ...
          }
      }
      

      Ի հակադրություն վերը նշված եղանակի՝ Spring Boot «հենքում» (framework) հասցեական ուղղորդումը կարգավորվում է անմիջականորեն պատասխանատու կլասերում՝ նվազեցնելով կոդի կրկնօրինակումը և հնարավոր վրիպակների հավանականությունը։ Սակայն, ինչպես գիտենք, ավտոմատացման յուրաքանչյուր փորձ անխուսափելիորեն առաջացնում է վերացականության հավելյալ մակարդակ։ Բերված օրինակում անոտացիաների շնորհիվ կատարվում է հասցեավորման և պարամետրերի դուրսբերման ավտոմատ կարգավորում, սակայն, ինչպես նշվեց, առանց համապատասխան դոկումենտացիայի դժվար է պատկերացնել կազմավորվող տրամաբանությունը։

      // imports...
      
      @RestController
      @RequestMapping("/game")
      class GameController {
      
          @GetMapping("/{gameHex}")
          @ResponseBody
          public Person getGame(@PathVariable String gameHex) {
              // ...
          }
      
          @PostMapping
          @ResponseBody
          public void startGame(@RequestParam String boardSize) {
              // ...
          }
      }
      

      Վերը նշված և հաջորդող օրինակները չեն ձգտում լինել լեզվական կամ այլ միջավայրի համատեքստում կանոնիկ։ Ավելին՝ նույնիսկ միևնույն «հենքի» (framework) շրջանակներում կարող են կիրառվել նմանատիպ խնդիրների լուծման տարբեր մոտեցումներ։ Նպատակն է ընդգծել միևնույն խնդրի լուծման ձևաչափերի տարբերությունը։

      Հետևյալ օրինակը Python լեզվական միջավայրում վեբ֊հավելվածների նախագծման Django «հենքի» (framework) միջոցով հասցեական ուղղորդման եղանակներից է, որտեղ կլասի փոխարեն անմիջականորեն հղում է արվում պատասխանատու ֆունկցիային։

      # url.py
      
      from django.urls import path
      from . import views
      
      urlpatterns = [
          path('game/', views.game),
          path('game/<str:gamehex>', views.new_game))
      ]
      
      # views.py
      
      def game(request, response):
        # ...
      
      def new_game(request, response):
        # ...
      

      Հասցեների մեկից ավելի համընկնման դեպքում, օրինակ՝ /game/* և /game/custom, ընդունված է առաջնայնություն տալ «ամենաերկար» հասցեին (the longest matching URL pattern), սակայն չի բացառվում նաև որոշակի միջավայրին հատուկ այլ «համաձայնություն»։

    • Web Template System (Վեբ֊շաբլոնավորման համակարգ)

      Վեբ֊շաբլոնների մեխանիզմը թույլ է տալիս վերաօգտագործել վեբ֊էջերի (HTML) նախօրոք ձևավորված հատվածներ և, ներարկելով որոշակի տվյալներ, ամեն հարցմանն ի պատասխան գեներացնել համապատասխան էջ։

      Այստեղ նույնպես՝ տարբեր «հենքերի» (frameworks) միջավայրում ձևաչափը և շարահյուսությունը սահմանվում է նախապես ընդունված շաբլոնավորման համակարգի համաձայն։ JSP (JavaServer Pages) տեխնոլոգիայի համաձայն, օրինակ, շաբլոնավորումը կատարվում է իրեն հատուկ նշագրման եղանակով՝ վեբ֊էջում համակցելով Java լեզվով ծրագրավորված կոդ, որը կազմվում է (compile) հատուկ ծրագրադարանի շնորհիվ։

      Ի հակադրություն JSP տեխնոլոգիայի՝ կան շաբլոնավորման ունիվերսալ համակարգեր, որոնք ներդրված են վեբ֊ծրագրավորման ոլորտում հայտնի գրեթե բոլոր լեզուներում։ Այդպիսին է, օրինակ, Mustache համակարգը։ Շարահյուսությամբ նման համակարգ է Django «հենքի» (framework) շաբլոնավորման համակարգը, որը ներկայացված է հետևյալ օրինակում։

      <!-- new_game.html - Django Template System -->
      
      {% extends 'game.html' %}
      
      {% block title %}{{ title }}{% endblock title %}
      
      {% block content %}
      <h1>{{ title }}</h1>
      <p>Hello {{ username }}</p>
      {% endblock content %}
      

      Այստեղ հիմք է վերցվում game.html ֆայլը և կատարվում են տեղային (բլոկային) փոփոխություններ՝ «ներարկելով» նշված փոփոխականները։ Փոփոխականների արժեքները փոխանցվում են ուղղորդման (routing) համաձայն՝ հարցմանը պատասխան ձևավորելու համար պատասխանատու ֆունկցիաների կողմից։

      # views.py
      
      from django.shortcuts import render
      
      def new_game(requests):
          title = get_title()
          username = get_username()
          return render(request, 'new_game.html', {'title': title, 'username': username})
      
  • Front-end

    Կլիենտի հարցմանն ի պատասխան ուղարկվող տվյալները (ֆայլերը), արդյունքում, կազմում են այն բովանդակությունը, որը սահմանված է հարցման միջոցով։ Բրոուզերի (Firefox, Chrome, etc.) օգնությամբ կատարվող հարցումներին ուղարկվող տվյալները, որպես կանոն, օժանդակ տվյալների օգնությամբ կազմում են նաև բովանդակության ձևն ու հավելյալ ֆունկցիոնալ տրամաբանությունը վեբ֊էջի (web page) տեսքով։

    • HTML

      Այսպես, HTML (HyperText Markup Language) ֆայլը, որը, որպես բրոուզերի առաջնային հարցում, սահմանում է բովանդակության վիզուալ արտաբերման ամբողջ տրամաբանությունը, սահմանում է նաև հավելյալ մեդիա֊էլեմենտների (image, video, audio, etc.) և օժանդակ ֆայլերի (styles, scripts, etc.) հղումները և ներդրման ձևաչափը։

      Հետևյալ պարզագույն օրինակում ներդրված հատուկ թեգերի (tags - body, p, img, etc.) և ատրիբուտների (attributes - id, class, src, etc.) օգնությամբ սահմանվում է վեբ֊էջի բովանդակությունը և, մասնակիորեն, ձևը։ Սակայն այստեղ կա հավելյալ մեդիա֊էլեմենտի հարաբերական հղում՝ <img src=..., որի համար հարկավոր է կատարել հավելյալ հարցում և առանց որի էջի բովանդակությունը թերի է։

      Որպես XML (Extensible Markup Language) տվյալների վերացական ձևաչափի ենթատեսակ՝ HTML ձևաչափը, ի տարբերություն, սահմանում է կիրառման ենթակա բոլոր թեգերը (tags) և ատրիբուտները (attributes)։ Որոշակի համաձայնությամբ հնարավոր է կիրառել նաև կամայական ատրիբուտ։

      Հատուկ հղումների և մետատվյալների նշման համար օգտագործվում է head թեգի տիրույթը, իսկ body տիրույթում նշվում են վիզուալ արտաբերման ենթակա բովանդակությունը։ Արտաբերման թեգերը նույնականացնելու համար կիրառվում է id ատրիբուտը, որը բացառության կարգով չպետք է կրկնվի ամբողջ էջի շրջանակում։

      <!-- HTML5 example -->
      <!DOCTYPE html>
      <html>
        <head>
          <title>Example</title>
        </head>
        <body>
          <h1>Heading</h1>
      
          <p id="p1" class="text">First paragraph text</p>
      
          <p id="p2" class="text">Second paragraph text</p>
      
          <a href="https://www.google.com">Link to Google</a>
      
          <img src="img/image.jpg"> 
      
        </body>
      </html> 
      
      • Բրոուզերի կողմից վեբ֊էջի վերծանման արդյունքում բոլոր ուղղակի և անուղղակի արտաքին հղումները ենթակա են հավելյալ հարցման օգնությամբ ներբեռնման։
      • Թեգերի միջանկյալ դատարկ տեքստային տարածքները (space, tab, newline) անտեսվում են։ Տեքստային բովանդակության մեկից ավելի հաջորդական բացատները ձևավորում են մեկ բացատ։
    • CSS

      Վեբ֊Էջի բաղադրիչների ձևավորման համար կարելի է համապատասխան թեգին վերագրել style ատրիբուտը։ Սակայն, ավտոմատացման և ռելացիոն համակարգերի սկզբունքների համաձայն, հաճախ նպատակահարմար է խուսափել տեղային կիրառումից և դուրս հանել տրամաբանությունը։ Այստեղ օգնում է CSS (Cascading Style Sheets) ձևաչափը, որը կարելի է առանձնացնել հատուկ ֆայլի տեսքով կամ ներառել HTML ֆայլի մեջ՝ ըստ նպատակահարմարության։

      Հետևյալ օրինակում առաջին պարագրաֆի տեքստը նշվում է կարմիր, երկրորդ պարագրաֆինը՝ կապույտ, նաև՝ երկու պարագրաֆների տեքստերը նշվում են կուրսիվ (italic)։ Գույները նշվում են համաձայն ընդունված անվանական արժեքների (HTML Color Names), սակայն կարելի է օգտագործել նաև RGB 8-bit միջակայքի (0-255) 16-ական (hexadecimal) նշագրման արժեքներ։ Տվյալ օրինակում արժեքները կլինեն, համապատասխանաբար, #ff0000 և #0000ff (ընդունելի է նաև կրկնվող բոլոր զույգերի կրճատ նշագրումը՝ #f00 և #00f

      <!-- INLINE STYLES -->>
      <body>
        ...
        <p id="p1" class="text" style="color: red; font-style: italic;">First paragraph text</p>
      
        <p id="p2" class="text" style="color: blue; font-style: italic;">Second paragraph text</p>
        ...
      </body>
      

      Հաջորդ օրինակում կիրառվում է առանձին CSS ֆայլ, որտեղ հատուկ նշագրմամբ հղում է արվում id և class ատրիբուտներին։ Հաշվի առնելով այն, որ էջում առկա բոլոր պարագրաֆներն ունեն text կլասի ատրիբուտ՝ .text { ... } սահմանումը կարելի է փոխարինել բոլոր պարագրաֆներն ընդգրկող p { ... } թեգի սահմանմամբ։

      /* styles.css - EXTERNAL CSS */
      .text {
          font-style: italic;
      }
      
      #p1 {
          color: red;
      }
      
      #p2 {
          color: blue;
      }
      

      Ձևավորման առանձին CSS ֆայլը HTML ֆայլում ներառելու համար գլխամասի head թեգի տիրույթում հարկավոր է link թեգի օգնությամբ նշել ֆայլի ձևաչափը և հղումը։

      <!-- Link to EXTERNAL CSS -->
      <html>
        <head>
          <title>Example</title>
          <link rel="stylesheet" href="styles.css">
        </head>
        <body>
          ...
          <p id="p1" class="text">First paragraph text</p>
      
          <p id="p2" class="text">Second paragraph text</p>
          ...
        </body>
      </html>
      

      Ներդրված CSS սահմանումների դեպքում հարկավոր է ներառել այն head թեգի տիրույթում ներդրված style թեգի տիրույթում։

      <!-- INTERNAL CSS -->
      <html>
        <head>
          <title>Example</title>
          <style>
            ...
            <!-- CSS definitions -->
            ...
          </style>
        </head>
        ...
      </html>
      

      Ներդրման դեպքում ձևավորման սահմանումները ներբեռնվում են որպես առաջնային HTML ֆայլի մաս։ Այս դեպքում խնայում ենք օժանդակ ֆայլի հավելյալ հարցում, սակայն կորցնում ենք մեկ ընդհանուր ձևավորմանը հիմք ունենալու և տարբեր էջերից մեկ ձևավորման ֆայլի հղում անելու հնարավորությունը։

      Տարբեր սարքերի (էկրանների) ֆիզիկական և ռաստերային չափսերի առկայության պարագայում էջի էլեմենտների բացարձակ (px, pt, in, etc.) միավորներով չափսերի սահմանումը հաճախ կորցնում է որոշակիությունը։ Այդ պատճառով կարիք է առաջանում սահմանել տարբեր հարաբերական CSS միավորներ (CSS Units)։ Այսպես, օրինակ, տոկոսային % միավորով նշվում է ծնողական էլեմենտի նկատմամբ չափսը, իսկ em և rem միավորներով նշվում է, համապատասխանաբար, ծնողական և գլխավոր <html> թեգերի տեքստային չափսերի նկատմամբ հարաբերական չափսերը (1.2em = { font-size of parent tag } + 20%)։

      CSS սահմանումների շնորհիվ կարելի է ստանալ նաև անիմացիոն դինամիկ էֆեկտներ (CSS Animation)` առանց JavaScript ծրագրավորման, որը ներկայացված է հաջորդ ենթաբաժնում։

    • JavaScript (JS)

      Այս լեզուն ի սկզբանե ստեղծվել է բրուզերի (Firefox, Chrome, etc.) վիրտուալ մեքենայում (օպերացիոն համակարգից իզոլացված) աշխատելու և վեբ֊էջի էլեմենտների (DOM - Document Object Model) հետ համագործակցելու նպատակով։ Սակայն տեխնոլոգիական զարգացմանը և ժամանակակից պահանջներին զուգահեռ այն մնում է դեռևս միակ ծրագրավորման լեզուն (բացառությամբ WebAssembly լեզվի, որը ենթակա է կատարման մեքենայական հրամանների տեսքով), որն ընդունելի է բրուզերների կողմից։ Համարվում է EcmaScript (ES) սպեցիֆիկացիայի (լեզվի) կրող։

      Այսպես, օրինակ, նախորդ ենթաբաժնում ներկայացված CSS ձևավորումը կարելի է կցել որևէ գործողության (event), օրինակ՝ որևէ կոճակի սեղման (onclick event)։

      <html>  
        <head>
          ...
          <script src="script.js"></script> 
        </head>
        <body>
          ...
          <p id="p1">First paragraph text</p>
          ...
          <button onclick="makeRed()">Button</button>
          ...
        </body>
      </html>
      
      // script.js
      
      function makeRed() {
          // 'document' is a preloaded constant of DOM
          let p = document.getElementById('p1');
          p.style = "color: red;"
      }
      

      Ըստ նպատակահարմարության՝ կոդը, CSS սահմանումների նման, կարող է ներդրվել նաև վեբ֊էջի script թեգի միջակայքում (բացվող և փակվող թեգերի միջև)։ Այստեղ նույնպես կարող ենք խուսափել հավելյալ ֆայլ առանձնացնելու և հավելյալ հարցում կատարելու անհրաժեշտությունից, եթե այլ էջերի հետ ընդհանրացման կարիք չկա։ Ի տարբերություն style թեգի՝ script թեգը կարող է ներդրվել նաև body թեգի միջակայքում, ինչը կարող է թեթևացնել էջի վիզուալիզացիան՝ հատկապես էջի վերջում տեղակայելու պարագայում, երբ անմիջապես կատարվող գործողություն չի պահանջվում։

      JavaScript լեզվում ֆունկցիաները առաջին դասի օբյեկտ են։ Այսինքն կարող են փոխանցվել որպես արգումենտ, կցվել փոփոխականի, և այլն։ Վերը նշված օրինակում ֆունկցիան կարելի էր սահմանել որպես փոփոխականի հայտարարման արտահայտություն։ Տարբերությունն այն է, որ ֆունկցիաների հայտարարումները վիրտուալ մեքենայի կողմից բեռնվում են նախապես, իսկ արտահայտությունները՝ ըստ սահմանման հերթականության, այսինքն փոփոխականը որպես ֆունկցիա կարելի է գործարկել միայն հայտարարման տողից հետո։

      // makeRed() - calling here will produce ReferenceError
      
      let makeRed = function () {
          // ...
      }
      

      JavaScript լեզվի համակցմամբ վեբ֊էջը (DOM - Document Object Model) կարելի է ենթարկել ցանկացած փոփոխության՝ էլէմենտների ձևավորումից մինչև էլեմենտների ստեղծում և հեռացում։ Բացի այդ, կարելի է օգտագորցել բրոուզերի կողմից ներկայացվող բոլոր ինտերֆեյսներն ու մեթոդները՝ ցանցային համագործակցության նպատակով։

      function getData() {
      
          let xhr = new XMLHttpRequest();
      
          // Define response handler on ready state
          xhr.onreadystatechange = function () {
              let data = xhr.responseText;
              document.getElementById('p1').innerHTML = data;
          };
      
          // Open and send the request
          xhr.open('GET', '/game', true);   // true - asynchronous
          xhr.send();
      }
      

      Այս և այլ առօրյա նպատակների ավտոմատացման համար կան բազմապիսի ծրագրադարան֊փաթեթներ։ Դրանցից ամենահեղինակավորներից է JQuery փաթեթը, որը շարահյուսական հավելյալ գործիքակազմ է կրում։ Բոլոր փաթեթները անհրաժեշտ է ներառել այն վեբ֊էջում, որտեղ ակնկալվում է դրանց օգտագործումը։ JavaScript փաթեթների (ֆայլերի) հայտարարման հերթականությունը կարևոր է կախվածության տեսանկյունից։

      <head>
        ...
        <!-- Get from JQuery CDN -->
        <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> 
        ...
        <script src="script.js"></script> 
        ...
      </head>
      
      // script.js
      
      $.ajax('/game', {
          success: function (data, status, xhr) {
              $('#p1').html(data);
          }
      });
      

      Լինելով դինամիկ֊թույլ տիպիզացմամբ լեզու և ունենալով «պատրաստի» կատարման միջավայր, որն առկա է բոլոր անձնական օգտագործման համակարգիչներում, JavaScript լեզուն մեծ հեղինակություն է վայելում ծրագրավորման սկսնակ֊սիրողական մակարդակում։ «Արտադրական» մակարդակում ավելի հաճախ խուսափում են այս լեզվով ուղղակի ծրագրավորումից (հաճախ՝ նաև լեզվական ծանոթ միջավայրից դուրս չգալու նպատակով) և ընտրում են մեկ այլ լեզու՝ փոխադրման սկզբունքով (transpilation)։ Կան նաև հատուկ այդ նպատակով ստեղծված մի շարք լեզուներ, որոնք միտված են բարձրացնելու համեմատաբար մեծ պրոյեկտների արտադրական էֆեկտիվությունը՝ TypeScript, ClojureScript, Dart, Elm, և այլ։ Բացի դրանից, EcmaScript լեզվական նորույթները հաճախ երկար ժամանակ չեն իրականացվում բոլոր բրոուզերների կողմից։ Այդ պատճառով կիրառվում է նաև ES նոր տարբերակից համամատաբար հին տարբերակի փոխադրման պրակտիկա (օրինակ՝ ES7 -> ES5), որը ևս ենթադրում է հավելյալ գործիքակազմ։

      JavaScript լեզուն ի սկզբանե նախագծված է կատարման սխալների առավելապես անտեսման սկզբունքով, այսինքն՝ կատարման բացառությունները (runtime exceptions), այդ թվում՝ հաշվարկային ուղղակի սխալները, չպետք է խոչնդոտ հանդիսանան ընդհանուր կատարման հոսքի համար։ Այս մոտեցումն արդարացված էր նրանով, որ բրոուզերի վիրտուալ մեքենայում կատարվող հրահանգներն այն ժամանակ մեծամասամբ վերբերվում էին պատկերման ենթակա էջի վիզուալ մանիպուլյացիաներին, և այդ համատեքստում բրոուզերի անխափան աշխատանքը ավելի կարևոր էր, քան հաշվարկային որևէ սխալանք։

      2009թ․ ստեղծվել է Node.js պրոյեկտը, որը դուրս է հանել իզոլացված V8 (Google Chromium Project) վիրտուալ մեքենան և ադապտացրել է օպերացիոն համակարգերի հետ համագործակցությանը։ Ներկայումս այն իրենից ներկայացնում է ամբողջական էկոհամակարգ և ծրագրավորման միջավայր։ Այն դրական հատկությունները, որով ժամանակին աչքի էին ընկնում Node.js «հենքի» (framework) օգնությամբ ստեղծված վեբ֊սերվերները, այժմ հասանելի են նաև մի շարք այլ լեզվական միջավայրերում, սակայն JavaScript - Back-end ծրագրավորումը դեռևս մեծ հեղինակություն է վայելում լեզվական ապոլոգետների շրջանում (հաճախ՝ նույնպես լեզվական ծանոթ միջավայրից դուրս չգալու պատճառով)։

    • SPA (Single֊Page Application)

      Վերը նշված գործիքակազմը HTML վեբ֊էջերի հիմքում ստեղծում է գրեթե անսահմանափակ հնարավորություններ GUI (Graphical User Interface) մոդելավորման համար։ Այդ իսկ պատճառով, չնայած բրոուզերի կողմից էջի վերծանման և արտաբերման (render) արագագործության խնդիրների, հետզհետե ավելի մեծ հեղինակություն են վայելում վեբ֊հավելվածները։ Միտված լինելով փոխարինել «առանձին» (stand-alone) հավելվածները՝ վեբ֊հավելվածները ձգտում են վերացնել վիզուալ֊էրգոնոմիկ անջրպետը, օրինակ՝ տարբեր էջերի հաջորդական ներբեռնումը մեկ հավելվածի շրջանակում։

      Նշված խնդիրները չեզոքացնելու նպատակով հաճախ նպատակահարմար է ստեղծել մեկ էջի սահմաններում գործող հավելված, որը նոր էջ ներբեռնելու փոխարեն ձևափոխում է գործող էջի մոդելը։ Այդպիսի հավելվածները կոչվում են SPA (Single֊Page Application)։ Նման էջերի հիմքում, որպես կանոն, գործում են «հենքեր»՝ Angular, React, Vue.js և այլ, որոնք պատասխանատու են վեբ֊էջերի ամբողջական մոդելավորման, ղեկավարման և դինամիկ ներբեռնման համար։

      Օգտվողի տեսանկյունից՝ SPA հավելվածները գրեթե չեն տարբերվում համակարգչում տեղադրված լոկալ֊գործող այլ հավելվածներից։ Առհասարակ, եթե անտեսել քեշի (cache) առկայությունը բրոուզերում (Firefox, Chrome, etc.), ապա կարելի է պնդել, որ կլիենտն ամեն անգամ սերվերից ստանում է իրեն նախատեսված հավելվածը՝ լոկալ կամ հեռավար աշխատանքի նպատակով։

      Electron «հենքը» (framework) թույլ է տալիս նմանօրինակ վեբ-հավելվածները Chromium բրոուզերի հետ միասին փաթեթավորել մեկ ամբողջական «շարժական» հավելվածի տեսքով, որը կարելի է օգտագործել որպես լոկալ֊տեղադրված հավելված։ Այս տեխնոլոգիայի միջոցով են տրամադրվում Skype, Slack, Visual Studio Code, Discord և այլ հայտնի հավելվածներ՝ չնայած աշխատանքային կայունության հետ կապված խնդիրների և այն փաստի, որ մեկ֊երկու նման հավելվածներ կարող են զբաղեցնել ամբողջական օպերացիոն համակարգի հիշողություն։


3.18 Պրակտիկ

[2020-12-11]

Նպատակն է ստեղծել վեբ֊հավելված, որը կմեկտեղի անցած բոլոր տեխնոլոգիաները՝ լեզու, ալգորիթմներ, տվյալների բազա, ցանց, կլիենտ֊սերվեր փոխհամագործակցություն, և այլ։

Տվյալ օրինակում միտումնավոր կերպով չեն օգտագործվում ավտոմատացման այլ համակարգեր և «հենքեր» (frameworks)` տեխնոլոգիական աբստրակցիաները այս շրջանակում հնարավորինս ցածր մակարդակում պահպանելու և դիտարկելու համար։

Պրոյեկտը իրենից ներկայացնում է երկու հոգու համար նախատեսված սեղանի խաղի վեբ֊հավելված, որտեղ խաղային ամբողջ տրամաբանությունը տեղի է ունենում սերվերային մասում (back-end), իսկ օգտատերերը համագործակցում են սերվերից անուղղակիորեն ներբեռնվող և բրոուզերում աշխատող հավելվածի օգնությամբ (front-end)։

Հավելվածի աշխատանքը պատկերված է հետևյալ սխեմայում` Client-Server Diagram (սխեման վեկտորային է և ենթակա է անկորուստ մասշտաբայնացման, համակարգչով դիտելու դեպքում՝ Ctrl +, Ctrl - կամ Ctrl <scroll>

Lուծումները տարբեր հատվածներում տարբեր ձևով են ներկայացված՝ տարբեր մոտեցումներ ընդգրկելու համար։ Օրինակ սերվլետների (Servlet) դեպքում, երբ վիճակից ելնելով վերջինս կարող է հղում անել կոնկրետ URL-ի, օրինակ՝ (response.sendRedirect("/game...")), կամ հարցմանը ետ ուղարկել տեքստային տվյալ՝ ուղղակի կերպով նշելով բովանդակության տեսակը (Content Type), HTTP պրոտոկոլի համապատասխան ստատուսի կոդը (օրինակ՝ 200 կամ 404) և բուն բովանդակություն։

Հավելվածը լոկալ համակարգչում փորձարկելու և ծրագրային մասի հետ աշխատելու համար հարկավոր է տեղադրել IntelliJ IDEA հավելվածի Ultimate լիցենզիոն տարբերակը, քանի որ սերվերային մասը գրված է Java Servlet կոմպոնենտների օգնությամբ, իսկ անվճար Community տարբերակը JSP (JavaServer Pages) ֆայլերի հետ աշխատելու հնարավորություն չի տալիս։ Լիցենզիոն տարբերակը, բարեբախտաբար, կարելի է օգտագործել 30֊օրյա «փորձնական» ժամկետով։

Հավելվածի աշխատանքային միջավայրի ամբողջական կարգավորման համար հետևեք ստորև ներկայացված ցուցումներին։

  • Development Environment Setup

    Տեղադրեք անհրաժեշտ ծրագրային բաղադրիչները․

    • Տեղադրեք JDK 8 (Java Development Kit) կամ ավելի նոր տարբերակի փաթեթը՝ ըստ ցուցումների։
    • Տեղադրեք SQLite տվյալների բազայի կառավարման համակարգը՝ ըստ ցուցումների։
    • Տեղադրեք Git հավելվածը՝ ըստ ցուցումների։
    • Տեղադրեք Tomcat 8.5 սերվերային հավելվածը՝ ըստ ցուցումների։
    • Տեղադրեք IntelliJ IDEA ծրագրավորման ինտեգրված միջավայրի Ultimate տարբերակը՝ թողնելով տեղադրման ընթացիկ կարգավորումները լռելյայն։

    Եթե նշված բաղադրիչները հաջողությամբ տեղադրված են՝ անցեք հաջորդ մասին։

  • Application Deployment Setup
    • Լոկալ համակարգչում կրկնօրինակեք դասընթացների ծրագրային հավելվածների պահոցը.
      • Եթե պահոցը արդեն կրկնօրինակել եք՝ այդ դիրեկտորիայում կատարեք git pull հրամանը, որպեսզի սինքրոնիզացնեք պահոցում առկա փոփոխություններն ու ավելացումները,
      • Եթե պահոցը դեռ չեք կրկնօրինակել՝ նախապես անցեք համապատասխան հասցեով (որտեղ ենթադրվում է կրկնօրինակումը) և տերմինալի օգնությամբ գործարկեք git clone https://github.com/ingenium-am/cs-s3-2020 հրամանը,
    • Կարգավորեք IntelliJ IDEA միջավայրը.
      • Բացեք (Open) կրկնորինակված պահոցի 3.18/sample-game դիրեկտորիան,
      • Եթե IDEA-ն առաջարկում է (ներքևի աջ անկյունում) ներբեռնել անհրաժեշտ ծրագրադարանները, ապա համաձայնվեք՝ սեղմելով Import հղմանը,
      • Կարգավորեք պրոյեկտի կառուցվածքը՝ File - Project Structure…,
        1. Prօject էջում տեղադրված JDK-ներից ընտրեք Project SDK: (1.8 կամ ավելի նոր), իսկ Project language level: լեզվի մակարդակը ընտրեք 8 (Lambdas, type Annotations, etc.),
        2. Սեղմեք OK,
      • IDEA-ի պատուհանի աջ եզրից բացեք Maven պանելը, այնուհետև հավելվածի Lifecycle ենթաբաժինը և գործարկեք package հրամանը (double֊click),
        1. ՈՒշադրություն դարձրեք, որ, IDEA-ի կարգավորման և «կազմման» (build) ֆայլերից (/<project>/target) բացի, պրոյեկտի «ծառում» (ձախ պանել) ստեղծվում են նաև /sqlite/sample_game.db և src/main/webapp/WEB-INF/db.properties ֆայլերը, որոնք ծրագրված են ծառայել կարգավորված միջավայրի շրջանակներում (հատուկ են լոկալ համակարգչի միջավայրին) և ենթակա չեն սինքրոնիզացման (տես՝ .gitignore ֆայլ),
      • Գործիքների պանելից սեղմեք Add Configurations… (կամ Run - Edit Configurations… մենյուից),
        1. «Պլյուս» [+] նշանի օգնությամբ ավելացրեք Tomcat Server - Local,
        2. Server էջում կարգավորեք Application Server դաշտը,
          • Եթե առաջին անգամ եք կարգավորում՝ սեղմեք Configure… կոճակը։ Այնուհետև «պլյուս» [+] նշանի օգնությամբ ավելացրեք համակարգում առկա Tomcat սերվերային հավելվածներից մեկը՝ Tomcat Home: դաշտում նշելով հավելվածի դիրեկտորիան (<path>/apache-tomcat-<version>),
          • Սերվերը նշելուց հետո պատուհանի ներքևում հայտնվելու է Warning: No artifacts marked for deployment գրությունը։ Սեղմեք դիմացի Fix կոճակը և ընտրեք sample-game:war, որից հետո պետք է բացվի Deployment Էջը։
        3. Deployment էջում Application context: դաշտը փոփոխեք որպես /,
        4. Սեղմեք OK, կոնֆիգուրացիայի կոճակը գործիքների պանելում կստանա համապատասխան տեսք։
    • Գործարկեք կարգավորված հավելվածը.
      • Գործիքների պանելում, նոր ստեղծված կոնֆիգուրացիայի անվանման կողքից, գործարկեք Run կամ Debug հրամանները։ Breakpoint (նշումներ, որտեղ ծրագրի աշխատանքը կանգ է առնում) օգտագործելու պարագայում նախընտրելի է երկրորդը։
      • Սերվերի աշխատանքը դադարեցնելու համար գործարկեք Stop կոճակը։

      Պրոյեկտին սկսեք ծանոթանալ pom.xml (Maven - արտաքին ծրագրադարանների և կազմման ավտոմատացում) և src/main/webapp/WEB-INF/web.xml (Tomcat - Deplyment Descriptor, Servlet Mapping) ֆայլերի օգնությամբ։

      IntelliJ IDEA Ultimate տարբերակում առկա է նաև առանձին կառավարման պանել և տվյալների բազաների հետ աշխատանքի հավելյալ գործիքներ (աջ եզրում)։

      Պրոյեկտի dbservices.dao Java փաթեթում առկա են SQL շարահյուսությամբ արտահայտություններ, որոնք մկնիկի սլաքի տակ նշվում են «չկարգավորված»՝ SQL Dialect is not configured… այս դեպքում պետք է նույն գրության միջոցով ընտրել Change dialect to… և Generic SQL դաշտը փոխել SQLite:

      Շարահյուսությունը կարգավորելուց հետո IDEA-ն փորձելու է կապ հաստատել տվյալների բազայի հետ և ընդգծելու է SQL արտահայտությունը.
      Կարող եք օգտվել տվյալների բազաների կառավարման գործիքներից

      • Նշեք արտահայտությունը սկզբնամասում և հուշումների «կարմիր լամպի» օգնությամբ գործարկեք Configure data source հրամանը,
      • Այնուհետև՝ Database - [+] - Data Source - SQLite,
      • Կարգավորման պատուհանի File դաշտում նշեք տվյալների բազայի ֆայլը <project>/sqlite/sample-game.db և սեղմեք Test Connection կոճակը,
      • Եթե առաջարկվում է ներբեռնել անհրաժեշտ մոդուլներ՝ համաձայնվեք,
      • Եթե տեստի արդյունքը դրական է՝ սեղմեք OK

      Կարող եք օգտվել տվյալների բազաների կառավարման գործիքներից համապատասխան պանելի օգնությամբ։

      Պրոյեկտում փոփոխություններ կատարելուց և սերվերային համակարգը գործարկելուց հետո բրուզերում (client - Firefox, Chrome, etc.) հաճախ օգտագործվում է ստատիկ ֆայլերի (front-end - *.html, *.js, *.css, etc.) քեշավորված (cached) հին տարբերակը:

      Խնդիրների դեպքում խորհուրդ է տրվում զրոյացնել էջին պատկանող քեշը (cache) Ctrl-Shift-R գործողությամբ, կամ բրոուզերի համապատասխան կարգավորումներում, ինչպես նաև՝ ամբողջությամբ մաքրել վերակազմության հետքերը (Maven - Livecycle - clean

      Սերվերային կարգավորումների արդյունքում Deployment ֊ Application context: պարամետրը, որը պատասխանատու է հավելվածի հասցեի (URL) համար, ավտոմատ կերպով նշում է ծրագրի անվանման վերջածանցով՝ /<app-name>, ինչը խնդիրներ կարող է առաջացնել։ Ինչպես նշված է կարգավորրումների բաժնում՝ պրոյեկտի շրջանակներում այս պարամետրը պետք է լինի առանց վերջածանցի՝ / արժեքով։

      Ընդունված է հասցեների բոլոր կարգավորումները կատարել հարաբերականորեն՝ կցելով Application context պարամետրին (ինչպես, օրինակ, JSP ֆայլերում է կցվում request.getContextPath() մեթոդի արդյունքին)։ Սակայն այս պրոյեկտում տվյալ մոտեցումը լիարժեք կերպով կիրառված չէ։


ԸՆԴՀԱՆՈՒՐ ՀՂՈՒՄՆԵՐ

Միջավայրի կարգավորում

Python

[2020-06-02]

Official website, Wikipedia

Միջավայրի կարգավորման համար, հարկավոր է ներբեռնել ծրագրավորման լեզվի ամբողջական փաթեթը, որը պարունակում է նաև ծրագրավորման ինտեգրված միջավայր` IDE (Integrated Development Environment)։

Windows համակարգերում հավելվածի տեղադրման գրաֆիկական ինտերֆեյսով տարբերակներում հարկավոր է նշել Add Python <version> to PATH այնուհետև ընտրել Customize installation տարբերակը, որտեղ հարկավոր է նշել pip և tcl/tk and IDLE, եթե նշված չէ։

Ներբեռնելու համար անցեք հետևյալ հղմամբ՝
Python (Downloads)

GNU/Linux համակարգերի դեպքում նախընտրելի է փաթեթը տեղադրել հասանելի package-manager-ի օգնությամբ։ Օրինակ՝ apt հրամանով կամ Ubuntu Software Center ծրագրի օգնությամբ (Debian/Ubuntu based), yum (Red Hat based) և pacman (Arch based) հրամաններով, և այլ։

  • IDLE - Integrated Development and Learning Environment
    Windows
    Ծրագրերի ցանկում փնտրեք Python խումբը, և այդ ենթախմբում՝ IDLE ։
    GNU/Linux
    Հարկավոր է ստեղծել ծրագրային «պիտակ» (shortcut) idle հրամանի համար։
    Համակարգում պետք է տեղադրված լինի tk ծրագրային փաթեթը։
  • Տերմինալի միջոցով python հրամանը գործարկելուց կարող է ենթադրվել Python 2 ։ Որոշ օպերացիոն համակարգերում 3-րդ սերնդի համար հարկավոր է գործարկել python3 հրամանը։
  • Տարբերակը տեսնելու համար հարկավոր է գործարկել python --version հրամանաը։

Java

[2020-07-30]

Official website, Wikipedia

Միջավայրի կարգավորման համար հարկավոր է ներբեռնել Java լեզվի ծրագրավորման փաթեթը՝ JDK (Java Development Kit)։

  • Պաշտոնական կայքի գլխավոր էջերում ներբռնման (Java Download) հղում է արվում հիմնականում JRE (Java Runtime Environment) փաթեթին՝ առանց դրա մասին տեղեկացնելու, ինչը ենթադրում է արդեն առկա ծրագրի կատարման համար անհրաժեշտ հավելվածների նվածագույն տարբերակ` ներառյալ JVM (Java Virtual Machine)։ Տվյալ տարբերակը չի կարող ծառայել որպես ծրագրավորման միջավայր։

Խորհուրդ է տրվում ներբեռնել և օգտագործել «բաց֊աղբյուրով» (open-source) և GPL լիցենզավորմամբ OpenJDK տարբերակը։

Windows
  • Խորհուրդ է տրվում ներբեռնել AdoptOpenJDK փաթեթը, որը ենթադրում է ավտոմատ տեղադրում և կարգավորում։
  • Ներբեռնման էջում առաջարկվում է ընտրել տարբերակը և JVM վիրտուալ մեքենան։ Խորհուրդ է տրվում ընտրել HotSpot վիրտուալ մեքենա։ Տարբերակը՝ ըստ հարմարության։
Linux/MacOS
  • Խորհուրդ է տրվում օգտվել փաթեթների տեղադրման առկա հավելվածներիծ (e.g. Apt for Debian, Homebrew for MacOs):

OpenJDK փաթեթը կարող եք տեղադրել նաև հետևալ կերպ․

  1. Ընտրեք և ներբեռնեք գործող տարբերակներից որևէ մեկը հետևյալ հղմամբ՝ Download OpenJDK։
  2. Zip արխիվային ֆայլը ներբեռնելուց հետո ապաարխիվացրեք համակարգի ծրագրային որևէ հասցեով (օր․ Windows: C:\Users\Public\, GNU/Linux, macOS: /opt/)։ Ապաարխիվացնելուց հետո ընտրված հասցեում կստեղծվի jdk-<version> նոր դիրեկտորիա (<version> = տարբերակի թողարկման համարը

Windows օպերացիոն համակարգում օգտատիրոջ կողմից գործարկվող հավելվածներին թույլ չի տրվում ուղղակիորեն (առանց թույլտվության հաստատման - UAC Prompt) փոփոխություն կատարել «համակարգային» (system) դիրեկտորիաներում (օր․՝ C:\Program Files\ կամ C:\Program Files (x86)\)։ Այդ պատճառով տարբեր հավելվածներ նշված հասցեով տեղակայվելու դեպքում իրենց ֆունկցիոնալը աբողջությամբ չեն կարողանում կիրառել, ինչը տարբեր սխալանքների պատճառ է դառնում։

  1. Կատարեք միջավայրի հետևյալ փոփոխություններ (Windows համակարգերում՝ Advanced System Settings - Environment Variables - System variables).
    • PATH փոփոխականին ավելացրեք ապաարխիվացման հասցեն՝ ավելացնելով bin դիրեկտորիան (օրինակ՝ C:\Users\Public\Java\jdk-<version>\bin
    • Ստեղծեք JAVA_HOME նոր փոփոխական` ապաարխիվացման հասցեի արժեքով՝ ԱՌԱՆՑ bin դիրեկտորիայի (օրինակ՝ C:\Users\Public\Java\jdk-<version>
  2. Տեղադրեք նախընտրելի խմբագիր կամ IDE (օրինակ` IntelliJ IDEA) և կարգավորեք JDK֊ի հասցեն։

Կարգավորումները փորձարկելու համար կարող եք տերմինալի միջոցով կատարել java -version հրամանը։

Git

[2020-12-29]

Windows
  • Ներբեռնեք տեղադրման փաթեթը օֆիցյալ էջից։
  • Տեղադրման ընթացքում Configuring the line ending conversions էջի կարգավորումը հարկավոր է փոխել՝ Checkout as is, commit as is, ընթացիկ այլ կարգավորումները կարող եք թողնել լռելյայն։ Այս տարբերակում առկա է նաև GitGUI հավելված։
  • Փորձարկեք կարգավորումը GitBash տերմինալում git --version հրամանը գործարկելով (Command Prompt հավելվածը օգտագործել խորհուրդ չի տրվում)։
Linux/MacOS
  • Խորհուրդ է տրվում օգտվել փաթեթների տեղադրման առկա հավելվածներիծ (e.g. Apt for Debian, Homebrew for MacOs):
  • Ապաարխիվացրեք և տեղադրեք որևէ հասցեով (օր․ С:\Users\<user> կամ C:\Users\Public
  • Փորձարկեք կարգավորումը տերմինալում git --version հրամանը գործարկելով։

SQLite

[2020-12-29]

Windows
  • Ներբեռնման էջում Precompiled Binaries for Windows եմթաբաժնից ընտրեք sqlite-tools-<version>.zip ֆալյը։
  • Ապաարխիվացրեք և տեղադրեք որևէ հասցեով (օր․ С:\Users\<user> կամ C:\Users\Public
  • Կատարեք միջավայրի հետևյալ փոփոխություններ (Windows համակարգերում՝ Advanced System Settings - Environment Variables - System variables).
    • PATH փոփոխականին ավելացրեք նախորդ կետին համապատասխան ապաարխիվացման հասցեն։
Linux/MacOS
  • Խորհուրդ է տրվում օգտվել փաթեթների տեղադրման առկա հավելվածներիծ (e.g. Apt for Debian, Homebrew for MacOs):

Կարգավորումը փորձարկելու համար կարող եք տերմինալի միջոցով կատարել sqlite3 --version հրամանը։

Tomcat Server

[2020-12-29]

  • Ներբեռնեք Tomcat հավելվածը՝ անցնելով համապատասխան տարբերակի էջ։ Հղումների Core: ենթաբաժնից ներբեռնեք արխիվային ֆայլը։ Windows ՕՀ դեպքում՝ համակարգի բիթայնության համապատասխան Windows zip տարբերակը (Windows Service Installer ֆայլը պետք ՉԷ ներբերռնել)։
  • Ապաարխիվացրեք և տեղադրեք որևէ հասցեով (օր․ С:\Users\<user> կամ C:\Users\Public
  • Հավելվածը պատրաստ է օգտագործման համար։

Ինտեգրված ծրագրավորման միջավայրների ցանկ - IDEs

Առցանց (Online) համակարգեր

[2020-08-24]

Name Languages Notes
AWS Cloud9 IDE C++, Java, Python, Haskell, Web stack, etc. Support level may differ
Repl.it C/++/#, Java, Python, Web stack, etc. (62) Language list
Eclipse Che C/++/#, Java, Python, Web stack, etc. Supports LSP (Language Server Protocol)
Codeanywhere C/++/#, Java, Python, Web stack, etc. (100+) Pricing (Free Trial for 7 days)

Ծրագրային հավելվածներ

[2020-08-24]

Name Languages OS Notes
Visual Studio C/++/#, Java, Python, Java (Android), etc. Windows, MacOS Platform install support1, language support by plugins
Visual Studio Code2 Language-independent Windows, MacOS, Linux Language support by plugins
Xcode Language-independent MacOS Mac-oriented, language support by plugins
Emacs2 Language-independent Windows, MacOS, Linux Language support by plugins
Sublime Text2 Language-independent Windows, MacOS, Linux Language support by plugins
IntelliJ IDEA Java, JVM Languages, Java Frameworks, Web stack, etc. Windows, MacOS, Linux JVM-oriented, language support by plugins
PyCharm Python, Python Frameworks, Web stack, etc. Windows, MacOS, Linux Python-oriented, language support by plugins

1. Language platforms could be installed with IDE
2. Source-code editor with IDE capabilities


Հավելվածներ

1-Byte Counter

[2020-07-27]

Արտասովոր հիմքերով (երկուական, տասնվեցական) թվերի ընկալման համար դասընթացների մատյանի շրջանակներում տեղադրված է 1 բայթ «կրողունակությամբ» վիզուալ հաշվիչ։


Գրականություն և այլ հղումներ

Գրքեր

[2020-09-15]

  • SICP - Structure and Inerpratation of Computer Programs

    ISBN: 978-0262510875 (2nd edition) - Amazon

  • P.Seibel - Practical Common Lisp

    ISBN: 978-1590592397 - Amazon

  • P.Graham - ANSI Common Lisp

    ISBN: 978-0133708752 - Amazon

  • B.Kernighan, D.Ritchie - C Programming Language

    ISBN: 978-0131103627 (2nd edition) - Amazon

  • D.Griffiths - Head First C

    ISBN: 978-1449399917 - Amazon

  • B.Stroustrup - Tour of C++, A

    ISBN: 978-0321958310 - Amazon

  • P.Deitel, H.Deitel - Java How To Program (Early Objects)

    ISBN: 978-0133807806 (10th edition) - Amazon

  • K.Sierra, B.Bates - Head First Java

    ISBN: 978-0596009205 (2nd edition) - Amazon

  • J.Bloch - Effective Java

    ISBN: 978-0134685991 (3rd edition) - Amazon

  • M.Lutz - Learning Python

    ISBN: 978-1449355739 (5th edition) - Amazon

  • Z.Shaw - Learn Python 3 the Hard Way

    ISBN: 978-0134692883 - Amazon

  • M.Lipovaca - Learn You a Haskell for Great Good

    ISBN: 978-1593272838 - Amazon

  • V.Bragilevsky - Haskell in Depth

    ISBN: 978-1617295409 - Amazon

  • D․Higginbotham - Clojure for the Brave and True

    ISBN: 978-1593275914 - Amazon


ՏԵՐՄԻՆՆԵՐԻ ԲԱՌԱՐԱՆ

A

ALU

Arithmetic Logic Unit - Ենթապրոցեսոր՝ կենտրոնական պրոցեսորի (CPU) հիմնական մասերից, որը կատարում է թվաբանական և տրամաբանական գործողություններ, ինչպիսիք են՝ գումարումը, հանումը, բազմապատկումը, բաժանումը, և այլ։ Տես նաև՝ FPU։

API

(Apllication Programming Interface abbrev., Интерфейс Прикладного Программирования ռուս․) Ծրագրային տարբեր հավելվածների փոխինտեգրման միջոց, որը բնութագրում է երկու ծրագրային հավելվածների միջև փոխհաղորդակցման ձևաչափերն ու այլ համաձայնությունները։


B

Bare-metal

Դատարկ, «մերկ» սարքավորում՝ առանց վիրտուալիզացիայի և օպերացիոն համակարգերի։ ~ server ամբողջական սերվերային միավոր՝ որպես մեկ ֆիզիկական սարքավորում (ի տարբերություն վիրտուալիզացված համակարգերի, որտեղ մեկ ֆիզիկական միավորը կարող է ընդգրկել մեկից ավելի վիրտուալ համակարգեր)։

By Default

Տես՝ Default

Bottleneck

Ֆենոմեն, ըստ որի՝ ամբողջ համակարգի արտադրողականությունը և կարողությունները խիստ սահմանափակվում են մեկ կոմպոնենտի կարողությունների սահմաններում։


C

Case sensitivity

Բնորոշում է մեծատառ և փոքրատառ տառերի տարբեր (case-sensitive) և նույնական (case-insensitive) լինելը։ Օրինակ՝ տարբեր ֆայլային համակարգերում Test.zip և test.zip ֆայլերի անվանումները կարող են դիտարկվել որպես տարբեր, կամ՝ որպես նույնական և, արդյունքում, միևնույն հասցեով տեղակայվելու պարագայում՝ հակադրվել միմյանց։

Circuit

Սխեմա: Electronic ~ էլեկտրոնային սխեմա, որը բաղկացած է առանձին էլէկտոնային մասերից, ինչպիսիք են՝ տրանզիստոր, կոնդենսատոր, դիոդ, դիմադրություն, և այլ։

CISC

Complex Instruction Set Computer - Կոմպլեքս հրամանների ցանկով համակարգիչ։ Ավելի հաճախ հանդիպում է x86 ընտանիքի պորոցեսորների տեսքով։ Տես նաև՝ RISC։

CLI

(Command-Line Interface abbrev.) Console, Terminal Syn. - Համակարգչի հետ համագործակցության տեքստային ինտերֆեյս, որը թույլ է տալիս հրամանների տողի միջոցով ներմուծել հրամաններ և ստանալ վերջինների արտաբերման արդյունքը։ Առավել հաճախ հանդիպում է որպես օպերացիոն համակարգերի առաջնային ինտերֆեյս, օրինակ՝ sh, bash (UNIX-like) և cmd (Windows):


D

Default

(По умолчанию ռուս․) Լռելյայն։ Պարամետրերի նախորոշված նշանակություններ, որոնք ծրագրային համակարգն օգտագործում է իր աշխատանքում, մինչև օգտվողի կողմից փոփոխության ենթարկելը։

Dependency

(Зависимость ռուս․) Ծրագրային հավելվածի կամ համակարգի կախվածություն մեկ այլ ծրագրային հավելվածից կամ փաթեթից։ Տես՝ Dependency hell։

DHCP

(Dynamic Host Configuration Protocol abbrev.) Ցանցային կառավարման «համաձայնագիր» (protocol), որտեղ DHCP սերվերը, օրինակ՝ ցանցային ուղորդիչներում (Router) գործող, ցանցին միացող բոլոր սարքվորումներին դինամիկ կերպով տրամադրում է IP հասցեներ և ցանցային կարգավորման այլ պարամետրեր։

DLL

(Dynamic-link library abbrev.) Microsoft Windows օպերացիոն համակարգերում ընդհանուր օգտագործման «դինամիկ֊կցմամբ» ծրագրադարաններ, որոնք ունեն ֆայլային անվանման .dll վերջածանց և կրում են .exe (executable) ֆայլերի ձևաչափը։ Գոյություն ունեն նույն վերջածանցով տվյալների հավաքածուներ, որոնք օգտագործվում են որպես ծրագրային տարբեր ռեսուրսներ։

DRY

(Don't Repeat Yourself abbrev.) «Մի՛ կրկնվիր»։ Ծրագրավորման սկզբունք, որն ուղղված է ծրագրում կոդի կրկնությունների կրճատմանը՝ նոր աբստրակցիաների կիրառման շնորհիվ։


E

EOF

(end-of-file abbrev.) Վիճակ օպերացիոն համակարգում, երբ տվյալների աղբյուրից տվայլներ այլևս չեն ներմուծվում։ Ստանդարտ մուտք֊ելքի (input-output) ֆունկցիաները վերադարձնում են արժեք, որը հավասար է "EOF" սիմվոլիկ հաստատունի` նշելու համար ֆայլի վերջը։ Իրական արժեքը բացասական ամբող թիվ է՝ կախված համակարգից (հիմնականում՝ -1), ինչը բացառում է տառանշային կոդի հետ համընկնումը։ Ստեղնաշարի օգնությամբ "EOF" մուտք անելու համար կարելի է օգտագործել կանխանծված կոմբինացիաներ՝ Ctrl-D (UNIX-like) և Ctrl-Z (Windows):

EOL

(end of line abbrev.) Newline, line ending, line feed, line break - Հատուկ տառանշան կամ դրանց հաջորդականություն տառանշային կոդավորման սպեցիֆիկացիայում (օր․ ASCII), որը նշում է տողի ավարտը և նոր տողի սկիզբը։ Տեքստային խմբագրման հավելվածները ↵ Enter սեղմելուց ավելացնում են համակարգին հատուկ համապատասխան "EOL"։ Տառանշանային արժեքները կախված համակարգից տարբերվում են, օրինակ՝ \n (LF - Line Feed) UNIX-like և UNIX-like-like համակարգերի համար, \r\n (CR+LF - Carriage Return and Line Feed) Windows համակարգերի համար։


F

Float

C լեզուների ընտանիքում ընդունված եզակի ճշգրտության (Single precision) տվյալի տիպ (data type)։ Երկուական ձևաչափում բաղկացած է 32 բիթից (4 բայթ)՝ 24 բիթ մանտիսայով (7 տասնորդական թվանշան)։

Floating-point

(Плавающая точка/запятая ռուս․) Լողացող կետ (կամ ստորակետ` կախված տասնորդական թվի բաժանարարի ընդունված նշանի)։ Իրական թվերի (R) ներկայացման ձև, որտեղ թիվը բաղկացած է մանտիսայից և էքսպոնենտից (աստիճանի ցուցիչից)։ Օրինակ՝ 1,0 x 106 կամ 1.0e106։ ~ arithmetic լողացող կետով թվերով թվաբանություն, որն ունի հարաբերական ճշգրտություն։ Տեղ ունի այնպիսի համակարգերում, որտեղ հարաբերական ճշգրտությունը փոխհատուցվում է արագ կատարմամբ։

FPU

Floating-Point Unit - Ենթապրոցեսոր՝ կենտրոնական պրոցեսորի (CPU) հիմնական մասերից, որը կատարում է համանման թվաբանական գրոծողություններ, ինչպիսին ALU-ն, սակայն լողացող կետով թվերով։ Տես՝ Floating-point։


G

GNU/Linux

FSF (Free Software Foundation) կողմից կազմված "GNU" գրեթե ամբողջական օպերացիոն համակարգի և "Linux" օպերացիոն համակարգի միջուկի (kernel) համադրություն, որը տարբեր պատրաստի համակարգերի տեսքով (Debian, Ubuntu, RedHat, Arch, etc.) տարածվում է որպես ամբողջական Օպերացիոն Համակարգ։
Գոյություն ունեն նաև "Linux" միջուկով այլ օպերացիոն համակարգեր (առանց՝ "GNU" ծրագրային մասի), օրինակ՝ Alpine Linux կամ Android ՕՀ֊երը։


I

IRQ

Ընդհատման պահանջ (Interrupt request անգլ․)։ Սարքային մակարդակում կենտրոնական պրոցեսորին տրվող ազդանշան (IRQ միացման օգնությամբ), որով ժամանակավորապես դադարեցվում է հիմնական կատարման ընթացքը և անցում է կատարվում նախորոշված հասցեում գտնվող Interrupt handler ծրագրային բաղադրիչի կատարման։


L

Lazy evaluation

«Ծույլ» (lazy անգ․) կատարումները ենթադրում են հավաքածուների կամ ռեկուրսիվ ֆունկցիաների ոչ ամբողջական կատարում՝ ցպահանջ։ Այսպիսով, ենթական ֆունկցիան (ծույլ) կատարման է ենթարկվում այնքան, որքան պահանջում է ծնողական ֆունկցիան, ինչպես գեներատոր ֆունկցիաների դեպքում։ Օրինակ` (pseudocode) take(2, range(1, 10^10)) արտահայտությամբ range ֆունկցիան չի գեներացնի 1֊ից մինչև 1010 բոլոր թվերի հաջորդականությամբ հավաքածու, քանի որ take ֆունկցիան առաջին արգումենտով` 2, նշում է հավաքածույի միայն առաջին 2 էլեմենտները վերադարձնելու պահանջը։
Հակադրվում է «խիստ» Strict evaluation կատարմանը։

M

Memory leak

Ծրագրավորողի և ծրագրի կողմից համակարգչի հիշողություն կառավարման սխալների արդյունքում առաջացած խնդիր, երբ հատկացված որոշակի հիշողության բաժինն այլևս չի օգտագործվում որևէ պրոցեսսի կողմից, սակայն որպես ռեսուրս ազատված չէ հետագա օգտագործման համար։


Օ

OCR

(Optical Charater Recognition abbrev.) Պատկերային տառանշանների փոխակերպումը տեքստային տարբերակի։ Օրինակ՝ սկանավորված կամ լուսանկարահանված ռաստերային ձևաչափով ֆայլերում առկա տեքստային բովանդակության վերծանում և փոխակերպում տեքստային ձևաչափի (txt, rtf, doc/docx, etc.


P

Pseudocode

(Псевдокод ռուս․) Ֆորմալ շարահյուսությամբ լեզու (հիմնականում՝ գոյություն ունեցող որևէ ծրագրավորման լեզվի նմանությամբ), որի օգնությամբ բնութագրվում է որոշակի ալգորիթմի կամ ծրագրային հավելվածի ընդհանուր տրամաբանությունը՝ հաճախ բաց թողնելով կատարման համար կենսական նշանակություն ունեցող որոշ բովանդակություն։


R

REPL

(Read-Eval-Print Loop abbrev.) «Կարդալ֊կատարել֊տպել» ցիկլ։ Ծրագրավորման միջավայր, որտեղ որոշակի լեզվական համակարգում ընդունում է օգտվողի կողմից մուտքագրված տվյալները, անմիջապես կատարում է և տպում է կատարման արդյունքը, այնուհետև վերադառնում է մուտքագրման պատրաստ վիճակի՝ պահպանելով կատարման միջավայրի փոփոխվող վիճակը։

RISC

Reduced Instruction Set Computer - Կրճատված հրամանների ցանկով համակարգիչ։ Ավելի հաճախ հանդիպում է ARM պրոցեսորների տեսքով՝ հիմնականում սմարթֆոններում և պլանշետներում։ Տես նաև՝ CISC։


S

SDK

(Software Development Kit abbrev.) Որոշակի ծրագրային միջավայրում ծրագրավորման համար անհրաժեշտ հավելվածների փաթեթ` ծրագրավորման «էկոհամակարգ» (compiler, debugger, etc.), որը տրամադրվում է տվյալ տեխնոլոգիայի «մատակարարի» (distributor) կողմից։

Standalone

~ application ծրագրային «ինքնուրույն» հավելված, որը չունի արտաքին ծրագրային կամ ցանցային կախվածություն։

State

(Состояние ռուս․) Համակարգչային կամ հաշվարկային սարքավորման այնպիսի «վիճակ», որը ժամանակի կտրվածքում հատուկ է որոշակի ծրագրային բաղադրիչներին և տեսականորեն վերարտադրելի է։

Stateful
State ունեցող, ~ system սարքավորման, ծրագրային կամ կոմպլեքս համակարգ, որի փոփոխված վիճակը մնայուն է համապատասխան միջավայրում։ Օրինակ՝ կոշտ սկավառակ (HDD) կամ տվյալների բազա (Database):
Stateless
State չունեցող. ~ system երբ համակարգը փոփոխության չի ենթարկվում, կամ փոփոխության ենթարկվելը տևում է որոշակի ժամանակ, որից հետո վերականգնվում է նախնական «վիճակը» (State)։ Օրինակ՝ համակարգչի օպերատիվ հիշողություն (RAM) կամ կոնտեյներիզացված (իզոլացված) ծագրային հավելված։

T

TDD

Test Driven Development - Ծրագրավորման պրոցես, որը ենթադրում է նախօրոք կազմվող թեստեր, որից հետո ստեղծվում է թեստերի պահանջները բավարարող հնարավոր մինիմալ կոդը։


U

UI

User Interface - Ծրագրային հավելվածի արտապատկերման այն ամբողջությունը, որի միջոցով կատարվում է օգտվողի հետ հիմնական փոխազդեցությունը, օրինակ՝ այն պատուհանը որի մեջ արտապատկերված կոճակների օգնությամբ որոշակի հրամաններ է գործարկում և պարամետրեր է փոփոխում, կամ՝ հատուկ առանձնացված դաշտերում տեքստ է մուտքագրում կամ գրաֆիկական պատկերներ է գեներացնում։


V

Variable extraction

Փոփոխականի ընդհանրացում այն դեպքերում, երբ փոփոխականը հայտարարվում է միևնույն արժեքով կամ ուղղակիորեն փոխանցվում է միևնույն արժեքի տեսքով մեկից ավելի դեպքերում։ Այդ պարագայում փոփոխականի հայտարարումը մասնավոր հատվածներից «դուրս է հանվում» ընդհանուր միջակայք (Scope)։

VCS

Version control system ֊ (Система управления версиями ռուս․) Ծրագրային «տարբերակների» ղեկավարման համակարգ։

Void

Դատարկ, փուչ։ ~ function ֆունկցիա կամ մեթող, որը ոչինչ չի վերադարձնում իրեն գործարկող ֆունկցիային, մեթոդին կամ արտահայտությանը։


X

x86

Intel կազմակերպության կողմից նախագծված ISA (Instruction Set Architecture), որը հիմնված Intel 8086 պրոցեսորների վրա. «x86» նշումը կապված է Intel կազմակերպության կողմից ստեղծված ապրանքային շարքին, որոնք հաջորդում են 8086 պրոցեսորը և որոնց անվանումն ավարտվում է «86»-ով (80186, 80286, 80386, 80486).

x86-64

(Հայտնի է նաև որպես x64, x86_64, AMD64 և Intel 64) Intel կազմակերպության կողմից ստեղծված x86 «ճարտարապետության» 64-bit տարբերակ, որը ներկայացնում է գործողությունների երկու «վիճակ» (mode)՝ 64-bit և «համատեղելիության» (compatibility).


Created: 2021-02-01 Mon 21:54

Validate