Лабораторна робота №4

Програмування списків мовами функціонального програмування

Мета

Опанувати теоретичні основи використання списків функціональними мовами та розробити програми обробки списків.

Виконав: студент групи ІПЗ-42
Лавріненко В.В.


Умова завдання

Варіант №14

14.1. Створити список шестикутних чисел, задавши їх кількість. Шестикутні числа складають послідовність 1, 6, 15, 28, 45,…. Формула для обчислення n-го шестикутного числа Pn = 2×n^2 - n. Вивести створений список. Виконати такі операції:
    a)  Знайти суму парних елементів списку;
    b)  Замінити в списку значення, кратні 5, на значення, помножені на 10.
    c)  Сформувати підсписок із чисел, кратних 10. 
.

14.2. Написати код, що моделює процес роботи роздрібної торгівлі. Фірма має в місті 6 точок роздрібного продажу. Попит на товари у цих точках 10 од. товару в день. Торгові точки обслуговуються оптовим магазином. На передачу запиту торгової точки в магазин потрібно 1 день. Товари за запитом поступають з оптового магазина в торгову точку в середньому через 5 днів після одержання запиту. Оптовий магазин кожні 14 днів розміщує замовлення на фабриці. Час, протягом якого магазин одержує вантаж із фабрики, в середньому складає 90 днів. Визначити обсяг запасу товару в оптовому магазині, ймовірність невдоволеного запиту торгової точки. Вивести на екран сценарій роботи роздрібної торгівлі.


Обгрунтування вибору середовища та мови функціонального програмування

Для розв'язку завдання було обрано мову Scheme та середовище DrRacket. Основними критеріями, за якими було зроблено вибір, є:

  • Наявність доволі великої кількості довідникових джерел інформації з синтаксису та поведінки даної мови. Це, в свою чергу, забезпечено й тим, що дана мова є діалектом розповсюдженої мови - LISP.

  • Відносна простота даної мови у засвоєнні, а середовища - використані, завдяки зрозумілому мінімалістичному користувацьому інтерфейсу, що надає найпоширеніший функціонал для написання та відлагоджування коду.


Структура програми (НІРО - діаграма)


Код програми

Завдання 14.1
#lang racket
; Лавріненко В.В.
; ІПЗ-42
; Л.р. 4, завдання 14.1

; процедура обчислення значення n-ого члена послідовності
(define (hexagonal-number n)
  (-(* 2 n n) n)
)

; процедура створення списку 6-кутних чисел
(define (make-list size)
  (make-list-inner '() size)
)

; процедура для рекурсивного створення списку 6-кутних чисел по елементу за виклик
(define (make-list-inner current-list size)
  ; якщо поточний - початковий - список порожій, проініціювати його першим елементом послідовності
  (cond [(null? current-list)
         (make-list-inner (list (hexagonal-number 1)) size)]
        ; якщо поточний список менший за заданий розмір, доповнити його наступним елементом послідовності
        [(< (length current-list) size)
         (make-list-inner (append current-list (list (hexagonal-number (+ 1 (length current-list))))) size)]
        ; якщо поточний список - заданого розміру, повернути його
        [(= (length current-list) size)
         current-list]
 )
)

; процедура для обчислення суми парних елементів списку
(define (even-member-sum list)
  (even-member-sum-inner list 0)
)

; внутрішня процедура для обчислення суми парнихз елементів у поточному підсписку
(define (even-member-sum-inner list current-sum)
  ; якщо поточний - кінцевий - список порожій, повернути поточну суму
  (cond [(null? list)
         current-sum]
        ; якщо перший елемент поточного списку - парний, викликати процедуру для решти списку та поточної суми + перший елемент
        [(even? (car list))
         (even-member-sum-inner (cdr list) (+ (car list) current-sum))]
        ; якщо перший елемент поточного списку - не парний, викликати процедуру для решти списку та поточної суми
        [(odd? (car list))
         (even-member-sum-inner (cdr list) current-sum)]
  ) 
)

; процедура для заміни елементів згідно з умовою
(define (replace-elements current-list)
  ; якщо поточний - кінцевий - список порожій, повернути поточний список
  (cond [(null? current-list)
         current-list]
        ; якщо поточний список починається з числа, кратного 5, повернути список, утворений з початкового елементу, помноженого на 10, та списку, отриманого викликом процедури для решти списку 
        [(= (modulo (car current-list) 5) 0)
         (append (list (* 10 (car current-list))) (replace-elements (cdr current-list)))]
        ; інакше повернути список, утворений з початкового елементу та списку, отриманого викликом процедури для решти списку        
        [else
         (append (list (car current-list)) (replace-elements (cdr current-list)))]
  ) 
)

; процедура для формування підсписку згідно з умовою
(define (get-subset current-list)
  ; якщо поточний - кінцевий - список порожій, повернути поточний список
  (cond [(null? current-list)
         current-list]
        ; якщо поточний список починається з числа, кратного 10, повернути список, утворений з початкового елементу та списку, отриманого викликом процедури для решти списку 
        [(= (modulo (car current-list) 10) 0)
         (append (list (car current-list)) (get-subset (cdr current-list)))]
        ; інакше повернути список, отриманоий викликом процедури для решти списку         
        [else
         (get-subset (cdr current-list))]
  ) 
)


(define size 20)
(define starting-list (make-list size))
(display "The starting list:\n")
(display starting-list)
(display "\n")
(display "Sum of the list's even members:\n")
(display (even-member-sum starting-list))
(display "\n")
(display "The list with all its members that are multiples of 5, multiplied by 10:\n")
(display (replace-elements starting-list))
(display "\n")
(display "The elements of the list that are multiples of 10:\n")
(display (get-subset starting-list))
					
Завдання 14.2
; Лавріненко В.В.
; ІПЗ-42
; Л.р. 4, завдання 14.2

; кількість товару в кожній точці
(define store-stock (list 50 70 100 30 40 90))
; тривалість очікування кожною точкою наступної поставки товару
(define store-restock-wait (list 0 0 0 0 0 0))
; статус відправки товару в кожну точку
(define store-restock-wait-status (list #f #f #f #f #f #f))
; кількість товару, що поставляєтсья в кожну точку
(define store-restock-size 65)
; кількість товару, що продається точкою за день
(define store-stock-loss 10)
; тривалість очікуання на поставку товару в точку
(define store-restock-delay 6)
; кількість товару в оптовому магазині
(define warehouse-stock 300)
; кількість товару, що поставляється в оптовий магазин за раз
(define warehouse-restock-size 1000)
; тривалість поставки товару в оптовий магазин
(define warehouse-restock-delay 90)
; тривалість часу між замовленнями поставок товару в оптовий магазин
(define warehouse-restock-frequency 14)
; черга з тривалості часу до поставки товару в оптовий магазин
(define warehouse-restock-queue '())
; дефіцит товару в оптових точках за день
(define deficit 0)
; накопичений дефіцит товару в оптових точках за тривалість симуляції
(define overall-deficit 0)

; процедура для отримання значення у списку за індексом
(define (get-list-element elements n) 
  (if (= n 1) 
      (car elements)
         (get-list-element (cdr elements) (- n 1 ))
  )
)

; процедура для встановлення значення у списку за індексом
(define (set-list-element elements n value) 
  (if (= n 1) 
      (append (list value) (cdr elements))
         (append (list (car elements)) (set-list-element (cdr elements) (- n 1 ) value))
  )
)

; основна процедура для симуляції роздрібної торгівлі
(define (simulate days)
  (simulate-internal 1 days)
)

; внутрішня процедура для симуляції роздрібної торгівлі за заданий день
(define (simulate-internal current-day days)
  ; поки не настав кінецвий день, викликаються процедури для симуляції окремих процесів торгівлі
  (cond [(< current-day days)
           (begin
             (sell-stock)
             (await-stock)
             (send-stock)
             (await-warehouse-restock)
             (send-warehouse-restock-order current-day)
             (print-status current-day)
             (simulate-internal (+ 1 current-day) days)
            )
         ]
        ; якщо настав кінецвий день, викликається процедура обчислення незадоволеного попиту за останній день та виведення інформації про незадоволений попит в цілому 
        [else
         (begin
           (calculate-deficit 1)
           (print-deficit)
         )]
  )
)
; процедура симуляції продажу товару за день
(define (sell-stock)
  (sell-stock-internal 1)
)

; внутрішня процедура симуляції продажу однією точкою товару за день
(define (sell-stock-internal n)
  ; товар продається доти, доки поточний індекс належить списку, і поточна точка має товар
  (cond[(and (< n 7) (or (> (get-list-element store-stock n)  store-stock-loss) (= (get-list-element store-stock n)  store-stock-loss)))
        (begin
        (set! store-stock (set-list-element store-stock n (- (get-list-element store-stock n) store-stock-loss)))
        (sell-stock-internal (+ n 1))
        )]
       ; якщо поточний індекс належить списку, але поточна точка має дефіцит товару, незадоволений попит додається до поточного показника незадоволеного попиту
       [(and (< n 7) (< (get-list-element store-stock n)  store-stock-loss))
        (begin
        (set! overall-deficit (+ overall-deficit (- store-stock-loss (get-list-element store-stock n))))
        (set! store-stock (set-list-element store-stock n 0))
        (sell-stock-internal (+ n 1))
        )]
   )
)

; процедура симуляції поставки товару в точки
(define (send-stock)
  (send-stock-internal 1)
)

; внутрішня процедура симуляції поставки товару в поточну точку
(define (send-stock-internal n)
  ; якщо поточна точка не очікує на поставку та час очікування становить 0, встановити показник очікування в істине значення, а тривалість очікування - у задане значення
  (cond[(and (< n 7) (or (> warehouse-stock store-restock-size) (= warehouse-stock store-restock-size)) (= (get-list-element  store-restock-wait n) 0)
             (not(get-list-element  store-restock-wait-status n)))
        (begin
        (set! warehouse-stock (- warehouse-stock store-restock-size))
        (set! store-restock-wait (set-list-element store-restock-wait n  store-restock-delay))
        (set! store-restock-wait-status (set-list-element store-restock-wait-status n #t))
        (send-stock-internal (+ n 1))
        )]
   )
)

; процедура симуляції очікування поставки товару в точки
(define (await-stock)
  (await-stock-internal 1)
)

; внутрішня процедура симуляції очікування поставки товару в задану точку
(define (await-stock-internal n)
  ; якщо тривалість очікування на товар не є нульовою, зменшити тривалість на 1
  (cond[(and (< n 7) (> (get-list-element  store-restock-wait n) 0))
        (begin
        (set! store-restock-wait (set-list-element store-restock-wait n (- (get-list-element  store-restock-wait n) 1)))
        (await-stock-internal (+ n 1))
        )]
      ; якщо тривалість очікування на товар є нульовою, і показник очікування поставлено в істине значення, додати до кількості товару на точці задану кількість, а показник зробити хибним 
       [(and (< n 7) (= (get-list-element  store-restock-wait n) 0) (get-list-element  store-restock-wait-status n))
        (begin
        (set! store-stock (set-list-element store-stock n (+ (get-list-element  store-stock n) store-restock-size)))
        (set! store-restock-wait-status (set-list-element store-restock-wait-status n #f))
        (await-stock-internal (+ n 1))
        )]
   )
)

; процедура симуляції відправлення товару в оптовий магазин
(define (send-warehouse-restock-order current-day)
  ; з заданою періодичністю викликається внутрішня процедура
  (cond[(= (modulo current-day warehouse-restock-frequency) 0)
        (send-warehouse-restock-order-internal)]
  )
)

; внутрішня процедура симуляції відправлення товару в оптовий магазин
(define (send-warehouse-restock-order-internal)
  ; до списку очікуваних поставок додається нова - з заданою тривалістю
  (set! warehouse-restock-queue (append warehouse-restock-queue (list warehouse-restock-delay)))
)

; процедура симуляції очікування оптовим магазином на поставку товару
(define (await-warehouse-restock)
  (await-warehouse-restock-internal 1)
)

; внутрішня процедура симуляції очікування оптовим магазином на поставку товару
(define (await-warehouse-restock-internal n)
  ; якщо очікування на поточну поставку не скінчилось, зменшити його тривалість на 1
  (cond[(and (< n (+ (length warehouse-restock-queue) 1)) (> (get-list-element  warehouse-restock-queue n) 0))
        (begin
        (set! warehouse-restock-queue (set-list-element warehouse-restock-queue n (- (get-list-element  warehouse-restock-queue n) 1)))
        (await-warehouse-restock-internal  (+ n 1))
        )]
       ; якщо очікування на поточну поставку скінчилось, прибрати перший елемент списку очікування, а кількість товару в оптовому магазині збільшити
       [(and (= n 1) (not (null? warehouse-restock-queue)) (= (get-list-element warehouse-restock-queue n) 0))
        (begin
        (set! warehouse-stock (+ warehouse-stock warehouse-restock-size))
        (set! warehouse-restock-queue (cdr warehouse-restock-queue))
        (await-warehouse-restock-internal  (+ n 1))
        )]
   )
)

; процедура обчислення кількості незадоволеного попиту між точками за день 
(define (calculate-deficit n)
    (cond[(and (< n 7) (< (get-list-element  store-stock n) store-stock-loss))
          (begin 
          (set! deficit (+ deficit (- store-stock-loss (get-list-element  store-stock n))))
          (calculate-deficit (+ n 1))
          )]
         [(< n 7)
          (calculate-deficit (+ n 1))]
    )
)

; процедура друку інформації про незадоволений попит  
(define (print-deficit)
  (begin
    (display "The amout of demand that will not be satisfied the next day: ")
    (display deficit)
    (display "\n")
    (display "Overall amout of demand that has not been satisfied: ")
    (display overall-deficit)
    (display "\n")
  )
)

; процедура друку інформації про результати продажів за поточний день 
(define (print-status current-day)
  (begin
    (display "Day ")
    (display current-day)
    (display "\n")
    (display "Current stock among the stores: ")
    (display store-stock)
    (display "\n")
    (display "Days left until the stores are restocked: ")
    (display store-restock-wait)
    (display "\n")
    (display "Current amount in-stock at the warehouse: ")
    (display warehouse-stock)
    (display "\n")
    (display "Pending factory orders: ")
    (display warehouse-restock-queue )
    (display "\n")
    (display "==============================================================\n")
  )
)

(display "\n")
(simulate 150)
(display "\n")
					


Скріншоти результатів


Оцінка достовірності результатів

  • Завдання 14.1 Правильність формування початкового списку було перевірено для 5 елементів. Так, отриманий результат відповідає заданому в умові завдання, а саме: (1 6 15 28 45). Аналогічно, завдання а) було перевірено для даного списку та отримано очікуваний результат: 34. Вочевидь, для завдання б) отримано очікуваний список: (1 6 150 28 450). Зррештою, правильність виконання завдання в) було перевірено для списку з 8 елементів, адже на 8 позиції маємо елемент 120 - перший в послідовності, що відповідає умові. Зрозуміло, що при цьому отримано список вигляду: (120).

  • Завдання 14.2 Правильність виконання програми було перевірено для низки аргументів для симуляції. Так, при цьому переконались у таких ключових пунктах реалізації: 
      - Продаж товару відбувається щодня в заданих межах попиту та не може перевищувати наявні у точці ресурси.
      - Поповнення запасів товару відбувається у заданих інтервалах, враховує кількість товару в оптовому магазині та збільшує кількість його у точках.
      - Аналогічно, поповнення запасів товару в оптовому магазині відповідає заданим в умові періодичним термінам та дійсно збільшує кількість його у магазині.
      - Симуляція тримає виключно у заданих межах часу.
      - Обчислені величини дефіциту відповідають спостереженим.
                    

Висновки

В лабораторній роботі було реалізовано обидва завдання, що передбачають, відповідно, маніпуляції зі списками та симуляцію економічної діяльності.

При програмній реалізації завдання 14.1 труднощів не постало: умова є цілком зрозумілою, тож виконання було доволі елементарним.

Певні труднощі виникли при розв'язанні завдання 14.2. Так, спершу виникали проблеми з розумінням умови завдання та, що важливіше, - з проектуванням розв'язку, оскільки аналогічні завдання із застосуванням принципів ООП було б розв'язано кардинально іншими шляхами із застосуванням поширених шаблонів проектування, що значно полегшило б розробку.

Також слід згадати, що при імплементації було допущено деякі поступки щодо реалістичності симуляції економічної діяльності, пов'язані, перш за все, з рівнем деталізації симуляції у масштабі роботи та з потенційними труднощами, що виникли б під час імплементації.