Lab1
+написан и протестирован код +подробный readme.md
This commit is contained in:
23
lab1/.github/workflows/test.yml
vendored
Normal file
23
lab1/.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: "28"
|
||||
gleam-version: "1.13.0"
|
||||
rebar3-version: "3"
|
||||
# elixir-version: "1"
|
||||
- run: gleam deps download
|
||||
- run: gleam test
|
||||
- run: gleam format --check src test
|
||||
4
lab1/.gitignore
vendored
Normal file
4
lab1/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.beam
|
||||
*.ez
|
||||
/build
|
||||
erl_crash.dump
|
||||
107
lab1/README.md
Normal file
107
lab1/README.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# lab1
|
||||
|
||||
### Описание задачи
|
||||
Цель: освоить базовые приёмы и абстракции функционального программирования: функции, поток управления и поток данных, сопоставление с образцом, рекурсия, свёртка, отображение, работа с функциями как с данными, списки.
|
||||
|
||||
В рамках лабораторной работы вам предлагается решить несколько задач [проекта Эйлер](https://projecteuler.net/archives). Список задач -- ваш вариант.
|
||||
|
||||
Для каждой проблемы должно быть представлено несколько решений:
|
||||
|
||||
1. монолитные реализации с использованием:
|
||||
- хвостовой рекурсии;
|
||||
- рекурсии (вариант с хвостовой рекурсией не является примером рекурсии);
|
||||
2. модульной реализации, где явно разделена генерация последовательности, фильтрация и свёртка (должны использоваться функции reduce/fold, filter и аналогичные);
|
||||
3. генерация последовательности при помощи отображения (map);
|
||||
4. работа со спец. синтаксисом для циклов (Gleam не применимо);
|
||||
5. работа с бесконечными списками для языков, поддерживающих ленивые коллекции или итераторы как часть языка (в Gleam только через сторонние библиотеки, а не часть языка);
|
||||
6. реализация на любом удобном для вас традиционном языке программирования для сравнения. (Kotlin)
|
||||
|
||||
Требуется использовать идиоматичный для технологии стиль программирования.
|
||||
|
||||
Содержание отчёта:
|
||||
|
||||
- титульный лист;
|
||||
- описание проблемы;
|
||||
- ключевые элементы реализации с минимальными комментариями;
|
||||
- выводы (отзыв об использованных приёмах программирования).
|
||||
|
||||
Примечания:
|
||||
|
||||
- необходимо понимание разницы между ленивыми коллекциями и итераторами;
|
||||
- нужно знать особенности используемой технологии и того, как работают использованные вами приёмы.
|
||||
|
||||
### Задачи к выполнению
|
||||
|
||||
## [Task 1](https://projecteuler.net/problem=1)
|
||||
If we list all the natural numbers below $10$ that are multiples of $3$ or $5$, we get $3, 5, 6$ and $9$. The sum of these multiples is $23$.
|
||||
Find the sum of all the multiples of $3$ or $5$ below $1000$.
|
||||
|
||||
## [Task 30](https://projecteuler.net/problem=30)
|
||||
Surprisingly there are only three numbers that can be written as the sum of fourth powers of their digits:
|
||||
$$\begin{align}
|
||||
1634 = 1^4 + 6^4 + 3^4 + 4^4\\
|
||||
8208 = 8^4 + 2^4 + 0^4 + 8^4\\
|
||||
9474 = 9^4 + 4^4 + 7^4 + 4^4
|
||||
\end{align}$$
|
||||
As $1 = 1^4$ is not a sum it is not included.
|
||||
The sum of these numbers is $1634 + 8208 + 9474 = 19316$.
|
||||
Find the sum of all the numbers that can be written as the sum of fifth powers of their digits.
|
||||
|
||||
### Проверка
|
||||
|
||||
```sh
|
||||
gleam run # Run the project
|
||||
gleam test # Run the tests
|
||||
```
|
||||
|
||||
```sh
|
||||
>gleam run
|
||||
Compiling lab1
|
||||
Compiled in 0.50s
|
||||
Running lab1.main
|
||||
======================================================================
|
||||
TASK 1: Sum of multiples of 3 or 5 below 1000
|
||||
======================================================================
|
||||
1. Tail recursion:
|
||||
Result: 233168
|
||||
2. Regular recursion:
|
||||
Result: 233701
|
||||
3. Modular (filter + fold):
|
||||
Result: 233168
|
||||
4. Map-based:
|
||||
Result: 233168
|
||||
|
||||
======================================================================
|
||||
TASK 2: Sum of numbers equal to sum of 5th powers of digits
|
||||
======================================================================
|
||||
1. Tail recursion:
|
||||
Result: 443839
|
||||
2. Regular recursion:
|
||||
Result: 443839
|
||||
3. Modular (filter + fold):
|
||||
Result: 443839
|
||||
4. Map-based:
|
||||
Result: 443839
|
||||
```
|
||||
|
||||
### И референс на kotlin
|
||||
```kotlin
|
||||
fun main() {
|
||||
// Task 1: Sum of multiples of 3 or 5 below 1000
|
||||
val sum1 = (1 until 1000).filter { it % 3 == 0 || it % 5 == 0 }.sum()
|
||||
println("Task 1: $sum1")
|
||||
|
||||
// Task 30: Sum of numbers equal to sum of fifth powers of their digits
|
||||
val sum30 = (2..443839).filter { num ->
|
||||
val digits = num.toString().map { it.digitToInt() }
|
||||
val powerSum = digits.sumOf { it.toDouble().pow(5).toInt() }
|
||||
powerSum == num
|
||||
}.sum()
|
||||
println("Task 30: $sum30")
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
Task 1: 233168
|
||||
Task 30: 443839
|
||||
```
|
||||
21
lab1/gleam.toml
Normal file
21
lab1/gleam.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
name = "lab1"
|
||||
version = "1.0.0"
|
||||
|
||||
# Fill out these fields if you intend to generate HTML documentation or publish
|
||||
# your project to the Hex package manager.
|
||||
#
|
||||
# description = ""
|
||||
# licences = ["Apache-2.0"]
|
||||
# repository = { type = "github", user = "", repo = "" }
|
||||
# links = [{ title = "Website", href = "" }]
|
||||
#
|
||||
# For a full reference of all the available options, you can have a look at
|
||||
# https://gleam.run/writing-gleam/gleam-toml/.
|
||||
|
||||
[dependencies]
|
||||
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
|
||||
gleam_regexp = ">= 1.1.1 and < 2.0.0"
|
||||
colored = ">= 1.0.2 and < 2.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
gleeunit = ">= 1.0.0 and < 2.0.0"
|
||||
15
lab1/manifest.toml
Normal file
15
lab1/manifest.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
# This file was generated by Gleam
|
||||
# You typically do not need to edit this file
|
||||
|
||||
packages = [
|
||||
{ name = "colored", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "colored", source = "hex", outer_checksum = "CAD716398FB2DA4C05DDA60871EC9A76D704E75FB2BF5B3EDC6D088D8D6663A6" },
|
||||
{ name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" },
|
||||
{ name = "gleam_stdlib", version = "0.65.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "7C69C71D8C493AE11A5184828A77110EB05A7786EBF8B25B36A72F879C3EE107" },
|
||||
{ name = "gleeunit", version = "1.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "CD701726CBCE5588B375D157B4391CFD0F2F134CD12D9B6998A395484DE05C58" },
|
||||
]
|
||||
|
||||
[requirements]
|
||||
colored = { version = ">= 1.0.2 and < 2.0.0" }
|
||||
gleam_regexp = { version = ">= 1.1.1 and < 2.0.0" }
|
||||
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
|
||||
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
|
||||
17
lab1/src/lab1.gleam
Normal file
17
lab1/src/lab1.gleam
Normal file
@@ -0,0 +1,17 @@
|
||||
import gleam/io
|
||||
import gleam/string
|
||||
import task1/task1
|
||||
import task2/task2
|
||||
|
||||
pub fn main() {
|
||||
io.println(string.repeat("=", 70))
|
||||
io.println("TASK 1: Sum of multiples of 3 or 5 below 1000")
|
||||
io.println(string.repeat("=", 70))
|
||||
task1.main()
|
||||
|
||||
io.println("")
|
||||
io.println(string.repeat("=", 70))
|
||||
io.println("TASK 2: Sum of numbers equal to sum of 5th powers of digits")
|
||||
io.println(string.repeat("=", 70))
|
||||
task2.main()
|
||||
}
|
||||
68
lab1/src/task1/task1.gleam
Normal file
68
lab1/src/task1/task1.gleam
Normal file
@@ -0,0 +1,68 @@
|
||||
import gleam/int
|
||||
import gleam/io
|
||||
import gleam/list
|
||||
|
||||
// 1. Хвостовая рекурсия
|
||||
pub fn sum_multiples_tail_recursive(limit: Int) -> Int {
|
||||
case limit {
|
||||
n if n <= 0 -> 0
|
||||
_ -> sum_multiples_tail_recursive_helper(limit - 1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
fn sum_multiples_tail_recursive_helper(current: Int, acc: Int) -> Int {
|
||||
case current {
|
||||
0 -> acc
|
||||
n if n % 3 == 0 || n % 5 == 0 ->
|
||||
sum_multiples_tail_recursive_helper(n - 1, acc + n)
|
||||
n -> sum_multiples_tail_recursive_helper(n - 1, acc)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Настоящая рекурсия
|
||||
pub fn sum_multiples_recursive(limit: Int) -> Int {
|
||||
case limit - 1 {
|
||||
n if n <= 0 -> 0
|
||||
n if n % 3 == 0 || n % 5 == 0 -> n + sum_multiples_recursive(n)
|
||||
n -> sum_multiples_recursive(n)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Модульная (filter и fold)
|
||||
pub fn sum_multiples_modular(limit: Int) -> Int {
|
||||
list.range(1, limit - 1)
|
||||
|> list.filter(fn(n) { n % 3 == 0 || n % 5 == 0 })
|
||||
|> list.fold(0, fn(acc, n) { acc + n })
|
||||
}
|
||||
|
||||
// 4. Map
|
||||
pub fn sum_multiples_map(limit: Int) -> Int {
|
||||
list.range(1, limit - 1)
|
||||
|> list.map(fn(n) {
|
||||
case n % 3 == 0 || n % 5 == 0 {
|
||||
True -> n
|
||||
False -> 0
|
||||
}
|
||||
})
|
||||
|> list.fold(0, fn(acc, n) { acc + n })
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let limit = 1000
|
||||
|
||||
io.println("1. Tail recursion:")
|
||||
let result1 = sum_multiples_tail_recursive(limit)
|
||||
io.println(" Result: " <> int.to_string(result1))
|
||||
|
||||
io.println("2. Regular recursion:")
|
||||
let result2 = sum_multiples_recursive(limit)
|
||||
io.println(" Result: " <> int.to_string(result2))
|
||||
|
||||
io.println("3. Modular (filter + fold):")
|
||||
let result3 = sum_multiples_modular(limit)
|
||||
io.println(" Result: " <> int.to_string(result3))
|
||||
|
||||
io.println("4. Map-based:")
|
||||
let result4 = sum_multiples_map(limit)
|
||||
io.println(" Result: " <> int.to_string(result4))
|
||||
}
|
||||
146
lab1/src/task2/task2.gleam
Normal file
146
lab1/src/task2/task2.gleam
Normal file
@@ -0,0 +1,146 @@
|
||||
import gleam/int
|
||||
import gleam/io
|
||||
import gleam/list
|
||||
import gleam/string
|
||||
|
||||
// 1. Хвостовая рекурсия
|
||||
pub fn sum_power_equals_tail_recursive(power: Int, max_limit: Int) -> Int {
|
||||
tail_recursive_helper(2, max_limit, 0, power)
|
||||
}
|
||||
|
||||
fn tail_recursive_helper(current: Int, max: Int, acc: Int, power: Int) -> Int {
|
||||
case current > max {
|
||||
True -> acc
|
||||
False -> {
|
||||
let digit_sum = digits_power_sum_tail(current, power)
|
||||
case digit_sum == current {
|
||||
True -> tail_recursive_helper(current + 1, max, acc + current, power)
|
||||
False -> tail_recursive_helper(current + 1, max, acc, power)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn digits_power_sum_tail(n: Int, power: Int) -> Int {
|
||||
digits_power_sum_acc(n, power, 0)
|
||||
}
|
||||
|
||||
fn digits_power_sum_acc(n: Int, power: Int, acc: Int) -> Int {
|
||||
case n {
|
||||
0 -> acc
|
||||
_ -> {
|
||||
let digit = n % 10
|
||||
let pow_val = int_pow(digit, power)
|
||||
digits_power_sum_acc(n / 10, power, acc + pow_val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn int_pow(base: Int, exp: Int) -> Int {
|
||||
case exp {
|
||||
0 -> 1
|
||||
_ -> base * int_pow(base, exp - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Настоящая рекурсия
|
||||
pub fn sum_power_equals_recursive(power: Int, max_limit: Int) -> Int {
|
||||
case max_limit < 2 {
|
||||
True -> 0
|
||||
False -> {
|
||||
let sum_of_powers =
|
||||
string.inspect(max_limit)
|
||||
|> string.to_graphemes()
|
||||
|> list.map(fn(c) {
|
||||
let assert Ok(d) = int.parse(c)
|
||||
recursive_pow(d, power)
|
||||
})
|
||||
|> list.fold(0, fn(acc, n) { acc + n })
|
||||
|
||||
case sum_of_powers == max_limit {
|
||||
True -> max_limit + sum_power_equals_recursive(power, max_limit - 1)
|
||||
False -> sum_power_equals_recursive(power, max_limit - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn recursive_pow(base: Int, exp: Int) -> Int {
|
||||
case exp {
|
||||
0 -> 1
|
||||
_ -> base * recursive_pow(base, exp - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Модульная (filter fold)
|
||||
pub fn sum_power_equals_modular(power: Int, max_limit: Int) -> Int {
|
||||
list.range(2, max_limit)
|
||||
|> list.filter(fn(n) {
|
||||
let sum_of_powers =
|
||||
string.inspect(n)
|
||||
|> string.to_graphemes()
|
||||
|> list.map(fn(c) {
|
||||
let assert Ok(d) = int.parse(c)
|
||||
modular_pow(d, power)
|
||||
})
|
||||
|> list.fold(0, fn(acc, x) { acc + x })
|
||||
sum_of_powers == n
|
||||
})
|
||||
|> list.fold(0, fn(acc, n) { acc + n })
|
||||
}
|
||||
|
||||
fn modular_pow(base: Int, exp: Int) -> Int {
|
||||
case exp {
|
||||
0 -> 1
|
||||
_ -> base * modular_pow(base, exp - 1)
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Map
|
||||
pub fn sum_power_equals_map(power: Int, max_limit: Int) -> Int {
|
||||
list.range(2, max_limit)
|
||||
|> list.map(fn(n) {
|
||||
let sum_of_powers =
|
||||
string.inspect(n)
|
||||
|> string.to_graphemes()
|
||||
|> list.map(fn(c) {
|
||||
let assert Ok(d) = int.parse(c)
|
||||
map_pow(d, power)
|
||||
})
|
||||
|> list.fold(0, fn(acc, x) { acc + x })
|
||||
|
||||
case sum_of_powers == n {
|
||||
True -> n
|
||||
False -> 0
|
||||
}
|
||||
})
|
||||
|> list.fold(0, fn(acc, n) { acc + n })
|
||||
}
|
||||
|
||||
fn map_pow(base: Int, exp: Int) -> Int {
|
||||
case exp {
|
||||
0 -> 1
|
||||
_ -> base * map_pow(base, exp - 1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let power = 5
|
||||
let max_limit = 354_294
|
||||
|
||||
io.println("1. Tail recursion:")
|
||||
let result1 = sum_power_equals_tail_recursive(power, max_limit)
|
||||
io.println(" Result: " <> int.to_string(result1))
|
||||
|
||||
io.println("2. Regular recursion:")
|
||||
let result2 = sum_power_equals_recursive(power, max_limit)
|
||||
io.println(" Result: " <> int.to_string(result2))
|
||||
|
||||
io.println("3. Modular (filter + fold):")
|
||||
let result3 = sum_power_equals_modular(power, max_limit)
|
||||
io.println(" Result: " <> int.to_string(result3))
|
||||
|
||||
io.println("4. Map-based:")
|
||||
let result4 = sum_power_equals_map(power, max_limit)
|
||||
io.println(" Result: " <> int.to_string(result4))
|
||||
}
|
||||
123
lab1/test/lab1_test.gleam
Normal file
123
lab1/test/lab1_test.gleam
Normal file
@@ -0,0 +1,123 @@
|
||||
import gleeunit
|
||||
import gleeunit/should
|
||||
import task1/task1
|
||||
import task2/task2
|
||||
|
||||
pub fn main() {
|
||||
gleeunit.main()
|
||||
}
|
||||
|
||||
// Task 1 tests
|
||||
pub fn task1_tail_recursive_test() {
|
||||
task1.sum_multiples_tail_recursive(10)
|
||||
|> should.equal(23)
|
||||
|
||||
task1.sum_multiples_tail_recursive(1000)
|
||||
|> should.equal(233_168)
|
||||
|
||||
task1.sum_multiples_tail_recursive(0)
|
||||
|> should.equal(0)
|
||||
|
||||
task1.sum_multiples_tail_recursive(1)
|
||||
|> should.equal(0)
|
||||
}
|
||||
|
||||
pub fn task1_recursive_test() {
|
||||
task1.sum_multiples_recursive(10)
|
||||
|> should.equal(23)
|
||||
|
||||
task1.sum_multiples_recursive(1000)
|
||||
|> should.equal(233_168)
|
||||
|
||||
task1.sum_multiples_recursive(0)
|
||||
|> should.equal(0)
|
||||
|
||||
task1.sum_multiples_recursive(1)
|
||||
|> should.equal(0)
|
||||
}
|
||||
|
||||
pub fn task1_modular_test() {
|
||||
task1.sum_multiples_modular(10)
|
||||
|> should.equal(23)
|
||||
|
||||
task1.sum_multiples_modular(1000)
|
||||
|> should.equal(233_168)
|
||||
|
||||
task1.sum_multiples_modular(0)
|
||||
|> should.equal(0)
|
||||
|
||||
task1.sum_multiples_modular(1)
|
||||
|> should.equal(0)
|
||||
}
|
||||
|
||||
pub fn task1_map_test() {
|
||||
task1.sum_multiples_map(10)
|
||||
|> should.equal(23)
|
||||
|
||||
task1.sum_multiples_map(1000)
|
||||
|> should.equal(233_168)
|
||||
|
||||
task1.sum_multiples_map(0)
|
||||
|> should.equal(0)
|
||||
|
||||
task1.sum_multiples_map(1)
|
||||
|> should.equal(0)
|
||||
}
|
||||
|
||||
pub fn task1_all_implementations_equal_test() {
|
||||
let limit = 1000
|
||||
let result1 = task1.sum_multiples_tail_recursive(limit)
|
||||
let result2 = task1.sum_multiples_recursive(limit)
|
||||
let result3 = task1.sum_multiples_modular(limit)
|
||||
let result4 = task1.sum_multiples_map(limit)
|
||||
|
||||
result1 |> should.equal(result2)
|
||||
result1 |> should.equal(result3)
|
||||
result1 |> should.equal(result4)
|
||||
}
|
||||
|
||||
// Task 2 tests
|
||||
pub fn task2_tail_recursive_test() {
|
||||
task2.sum_power_equals_tail_recursive(4, 10_000)
|
||||
|> should.equal(19_316)
|
||||
|
||||
task2.sum_power_equals_tail_recursive(5, 354_294)
|
||||
|> should.equal(443_839)
|
||||
}
|
||||
|
||||
pub fn task2_recursive_test() {
|
||||
task2.sum_power_equals_recursive(4, 10_000)
|
||||
|> should.equal(19_316)
|
||||
|
||||
task2.sum_power_equals_recursive(5, 354_294)
|
||||
|> should.equal(443_839)
|
||||
}
|
||||
|
||||
pub fn task2_modular_test() {
|
||||
task2.sum_power_equals_modular(4, 10_000)
|
||||
|> should.equal(19_316)
|
||||
|
||||
task2.sum_power_equals_modular(5, 354_294)
|
||||
|> should.equal(443_839)
|
||||
}
|
||||
|
||||
pub fn task2_map_test() {
|
||||
task2.sum_power_equals_map(4, 10_000)
|
||||
|> should.equal(19_316)
|
||||
|
||||
task2.sum_power_equals_map(5, 354_294)
|
||||
|> should.equal(443_839)
|
||||
}
|
||||
|
||||
pub fn task2_all_implementations_equal_test() {
|
||||
let power = 5
|
||||
let max_limit = 354_294
|
||||
let result1 = task2.sum_power_equals_tail_recursive(power, max_limit)
|
||||
let result2 = task2.sum_power_equals_recursive(power, max_limit)
|
||||
let result3 = task2.sum_power_equals_modular(power, max_limit)
|
||||
let result4 = task2.sum_power_equals_map(power, max_limit)
|
||||
|
||||
result1 |> should.equal(result2)
|
||||
result1 |> should.equal(result3)
|
||||
result1 |> should.equal(result4)
|
||||
}
|
||||
Reference in New Issue
Block a user