+написан и протестирован код
+подробный readme.md
This commit is contained in:
2025-11-15 17:29:28 +03:00
parent fe00161ced
commit 43d0df5555
9 changed files with 524 additions and 0 deletions

23
lab1/.github/workflows/test.yml vendored Normal file
View 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
View File

@@ -0,0 +1,4 @@
*.beam
*.ez
/build
erl_crash.dump

107
lab1/README.md Normal file
View 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
View 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
View 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
View 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()
}

View 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
View 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
View 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)
}