SLC21 Week1 - Learn more about variable types. Subroutines. Practice problems.

in #slc21w1sergeyk25 days ago (edited)

Всім привіт! Вітаю з відкриттям 21 сезону Steemit Learning Challenge.

Запрошую продовжити вивчати програмування, або долучитися тих хто тільки приєднався. З одного боку це продовження попереднього курсу в SEC20, тож хто приймав участь минулого разу можуть продовжити навчатися далі. А з іншого боку це і як самостійний курс, хоч вже і не варто його розглядати як для абсолютних початківців. Так як він потребує самих мінімальних знань, але він все рівно для тих, хто робить перші кроки.
Щоб більш впевнено себе почувати, особливо хто долучився зараз - можете переглянути мої попередні уроки (особливо 3-6) та домашні завдання. Посилання наведень в таблиці внизу допису.

image.png

Більш заглибимося в типи даних С/С++

Як ви вже знаєте мови поділяються на строго типізовані і не типізовані, мови зі статичною типізацією і з динамічною типізацією. Мова С/С++ належить до мов з строгою статичною типізацією змінних. Тобто змінні перед використанням слід оголосити і тип змінної не може бути перевизначений далі в коді. Це приводить до деяких неочевидних чудес.
Чудо перше: 7/2=3, 19/10=1 - це цілочисельне ділення. Компілятор не лише обчислює значення виразу 7/2, а ще й "обчислює" тип результату int/int = int. А якщо ділити ціле на ціле, то не вийде щось незвичне - результат буде теж цілий. І баг це такий, чи фіча(особливість)? Можливо баг який став фічею(особливість). Але маємо те, що маємо. Інколи це можна використати за потребою, а інколи слід обійти.
float f=7/2; тут проблема полягає в тому, що ще до того як заносити значення в змінну f воно, це значення, буде 3, а не 3.5.
В інших мовах існують спеціальні операції для такого цілочисельного ділення.

Як же це обійти? Як обчислити правильно?

Варто представити запис числа 7 не як ціле, а як дійсне значення - для цього слід написати 7.0, тобто float f=7.0/2; вже дасть очікуваний результат.

розглянемо такий код:

int a=7;
int b=2;
float f=a/b;

Як бути в цьому разі? Дехто з учнів мені пропонує написати int a=7.0; та це нічого не дасть.
Слід написати float f=(float)a/b; це примусова типізація, кастинг, тут змінна a в процесі обчислення змінює свій тип. Але сама змінна залишається свого оголошеного типу, зміна відбувається лиш в поточний момент, тимчасово і лише для цього виразу.

Чудо друге

char k=124; яке буде значення k коли ми виконаємо, k=k+1; - що за питання - буде 125,
а k++; - очевидно що 126
а ++k; - очевидно що 127
а k+=1; - очевидно що 128, адже це все різні способи збільшити k на 1.
Та остання відповідь не вірна. Буде не 128. Адже всі типи даних обмежені, тобто їх змінні мають обмежені діапазони, і максимальне значення для char це 127. То що буде при спробі його збільшити ще на одиницю?
Дехто зі студентів мені відповідають що раз 127 край, межа - то від k++; значення так і лишиться 127. Виходить так ніби комп'ютер відмовляється виконувати команду.

Інший приклад, якщо змінна unsigned char j=254; тощо буде після j++? Вірно буде - 255, а якщо ще раз j++? Буде 256? - Буде 0!

image.png

Уявіть що на годиннику 55 секунд, що покаже годинник якщо пройде 7 секунд? 62 секунди - адже 55+7=62, так?
Але ж ми знаємо що 62 секунд не буває. Так само і в комп'ютері в типі змінних unsigned char діапазон значень від 0 до 255, і 256 там просто не може бути фізично.
Сподіваюся вам не важко здогадатися що буде якщо unsigned char j=0; та зробити j--;??)
Такі чудеса я показав на типі даних char, unsigned char; В інших типах даних, навіть інших мовах схожа історія. Тільки діапазони інші.

Тепер гадаю зрозуміло як може бути що 100x3=44? Адже 100х3 =300, 300-256=44
Але скільки ж буде коли 1004? 1004=400(в діапазоні не поміститься), 400-256=144(в діапазоні не поміститься), 144-256=-112(в діапазон входить)

Чудо третє

0.1 != 0.1 або 0.1 + 0.2 != 0.3 Запам'ятайте! Але як таке може бути і чому?
Тут багато причин. Сама перша - це десяткові дроби: 1.356; 0.2343; 12.010101 ... але є ще звичайні дроби, або просте звичайне ділення. І як обчислити 1/3? Адже це буде 1/3=0.3333333333... = 0.(3) Тобто це нескінченний, періодичний десятковий дріб. А пам'ять комп'ютера скінченна((, і виходить ми не можемо записати 1/3 в пам'яті комп'ютера. Вірніше, точно не можемо записати. але ви здивуєтеся коли я скажу що в пам'яті комп'ютера навіть 0.1 не можливо записати точно.
Тут правда варто б вам пояснити що таке системи числення ми послуговуємося десятковою системою де аж десять цифр 0123456789, в комп'ютері ж лише дві цифри 01 . Тому наше скінченне 0.1 запишеться так, нескінченно.
0.1(10) = 0.0100110011001100...(2)

Дослідити це можна у такий спосіб

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{    
    cout << setprecision(50) << (float)1/(float)3.0 << endl;
    cout << setprecision(50) << (double)1/(double)3.0 << endl;
    cout << setprecision(50) << (long double)1/(long double)3.0 << endl;
    return 0;
}

Або ще простіше:

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{    
    cout << setprecision(50) << 1.f/3.f << endl;
    cout << setprecision(50) << 1./3. << endl;
    cout << setprecision(50) << 1.L/3.L << endl;
    return 0;
}

Значення 50 в setprecision(50) тут явно зайве бо перебільшене, такої великою точності немає в мові С, я хотів підкреслити недоліки зберігання дійсних чисел. для використання setprecision() слід підключити бібліотеку<iomanip>
хто пише код на С там це зробити простіше printf(".50f); для double та float і printf(".50Lf); для long double
для double правда кажуть що слід писати так printf(".50lf);, але кажуть що тут l не має значення(на противагу scanf)

Для floatточними є 6 знаків після коми(решта просто cміття-недостовірні дані), для double 15 знаків, і для long double 15, 18 або 33 знаки - в залежності від архітектури комп'ютера.

image.png

Хто хоче прочитає в інтернеті детальніше, головне тут запам'ятати що числа зберігаються з такими особливостями.
і що 0.1 + 0.2 != 0.3 Як ще тоді перевірити наприклад таке - if(a==0.3)...? Для цього слід відповісти на запитання - а що значить рівні(==)? Тобто наскільки точно вони мають бути рівні? Точність у математиці позначають ε - епсілон. А модуль числа в мові С/С++ визначається функцією fabs().
Тобто перевірка на рівність if(a==0.3)... запишеться так if(fabs(a-0.3<0.0001)..., де 0.0001 - точність з якою можна вважати що ці числа рівні.

Функції

Розглянемо ще один момент - функції.
Пригадайте задачу з минулого заняття що малює лінію з *

int main()
{
    int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

Тут int main() це і ж функція, на початку вивчення кажуть що по іншому і бути не може, ця функція обов'язкова. А тому її багато хто пише автоматично.
З одного боку це програма яка 'малює'/виводить ряд із 23 зірочок. Та що буде коли переписати main на line, програма перетвориться на підпрограму.

int line()
{
    int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

Але особливість мови С така що це тепер не буде працювати, адже main() відсутня. То допишемо її.

int line()
{
    int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

int main()
{
    return 0;
}

Після запуску нічого не відбудеться, адже наявність коду функції нічого не дає, необхідно її викликати.

int line()
{
    int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

int main()
{
    line();
    return 0;
}

Проте скільки б ми функцію не викликали вона весь час виводить 23 зірочки.
Якщо перенести int count=23; у рядок до оголошення функції - то дана функція стане універсальною. Вона буде виводити стільки зірок, скільки вказано в її параметрах

int line(int count)
{
    //int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

int main()
{
    line(23);
    line(10);
    return 0;
}

Отже функція дозволяє однією командою виконувати певну кількість команд. Це позбавляє від повторного написання коду.
Серед типів даних є особливий тип даних void. В мові С/С++ не стали поділяти підпрограми на процедури та функції - назвали все функціями. Тільки деякі функції нічого не повертають, а точніше повертають.... нічого. Це те що в інших мовах називалося процедурою. Тобто функція int line(int count) і не функція в буквальному розумінні, а процедура. То ж вона не має повертати ніякого (числового) значення. void line(int count) - має бути так.
В цьому місці варто було б сказати про формальні та фактичні параметри та про передачу в параметри функції значень змінних, а не самих змінних зараз це пропущу, а пізніше зроблю вставку

Приклад 1. Напишемо функцію яка підраховує кількість цифр в числі.

Digit_count(int n)
Приклад, проста задача: написати функцію. яка приймає число як аргумент, і повідомляє(виводить) кількість цифр у даному числі - Digit_count(int n)
Задача хоч і проста - проте часто виявляється складною для початківців.
Очевидно що слід оголосити

int Digit_count(int n)
{
     
}

image.png

А як підрахувати кількість цифр у числі?

Представимо що це число n=94665 легко видно що цифр п'ять, але як я їх підрахував? Спроби пояснити самому собі такі елементарні дії - досить важко. Можливо саме тому важко бо дуже легко підрахувати таке мале число.

Навіть якщо уявити щось реальне, ящик з п'ятьма яблуками, легше не стане. Уявимо тоді що в ящику багато яблук, як їх порахувати? Одного разу прийдемо до думки що яблука слід перекладати в іншу коробку, і так вони зменшуватимуться, а ми їх рахуватимемо. Отже ключова тут дія - 'зменшувати на одне' поки яблука є.

Перенесемо аналогію на число. Зменшувати число на 1 доки воно є. Але зменшувати не відніманням 1, а слід відкидати цифру кожен раз, як одне яблуко. А як з числа прибрати цифру, одну - поділити його на 10.

int Digit_count(int n)
{
   int k=0;
   while(n>0)
   {
     n=n/10;
     k++;
   }
   return k;//  cout<<k; 
}

Тобто доки число ще є, більше нуля while(n>0) ми його ділимо на 10 - тобто відкидаємо останню цифру, кількість цифр зменшується на одну, отже ще одну цифру ми врахували - k++; і цей цикл буде крутитися доти, доки в числі залишатимуться цифри.
Взагалі, коли ми пишемо функцію для чогось, то слід писати "мовчазну" функцію, тобто вона повинна нічого не виводити(коли про це явно не сказано) вона має повертати результат через return Але тут я в коментарі все тки написав cout<<k; так як на етапі навчання, або в процесі написання(відладки) такої функції можна робити такий вивід.

Приклад 2

Скільки шестизначних чисел таких що цифри першої половини більші за цифри другої половини.
По перше деякі задачі з допомогою комп'ютера вирішуються простим повним перебором. Тим паче що ми нещодавно вивчили цикли. То організуємо цикл що пробігає всі шестизначні числа від 100000 до 999999 включно. і хоч це краще зробити циклом for(int n=100000; n<=999999; n++) я скористаюся циклом while:
але в циклі while я часто забуваю робити n++;((

int n=100000, k=0;
while(n<=999999)
{
    
}

Раз ми вивчили функції то можна написати одну універсальну функцію яка видає за запитом вказану цифру числа: f(number, pos); яка на запит f(783091, 2) видасть 8, а можна написати шість функцій під кожну цифру з шести f1(number), f2(number), ...f6(number). Саме в цій задачі і з навчальною метою мені більш до вподоби другий варіант.

inline int f1(int number){ return number%1000000/100000; }
inline int f2(int number){ return number%100000/10000; }
inline int f3(int number){ return number%10000/1000; }
inline int f4(int number){ return number%1000/100; }
inline int f5(int number){ return number%100/10; }
inline int f6(int number){ return number%10/1; }

Тут я оголосив функції як inline, це порада компілятору вставити код функції безпосередньо в код програми. Тобто це функція лиш формально. Але якби код тіла функції був вставлений самим програмістом це б ускладнило читання коду. Зараз оголошувати inline функції не обов'язково, компілятор часто вирішує на власний розсуд, складну функцію як inline він не зробить, а дуже просту функцію може зробити вставкою(тобто inline) і сам, без явної вказівки. Обов'язкове правило для домашніх робіт одну з функції в домашній роботі оголосити як inline, будь яку.

if(  f1(n)>f4(n) && f1(n)>f5(n) && f1(n)>f6(n) &&
     f2(n)>f4(n) && f2(n)>f5(n) && f2(n)>f6(n) &&
     f3(n)>f4(n) && f3(n)>f5(n) && f3(n)>f6(n) )
     k++;

Домашнє завдання

Правила проведення

Публікувати можна на будь-якій мові, в будь якій спільноті чи просто у власному блозі, посилання на вашу роботу додайте сюди коментарем

Щоб я швидко знайшов, перевірив та оцінив ваші роботи залиште посилання в коментарі під цим текстом а в роботі поставите тег #slc21w1sergeyk

До всіх завдань код наводити скріншотом, не текстом. Демонструвати теж скріншотом результат роботи програми.

Будьте обережні стосовно ідеальних та надефективних рішень, звичайній людині, початківцю їх не легко найти.

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

Плагіат і використання ШІ заборонено.

Учасники мають бути перевіреними та активними користувачами платформи.

Використані зображення мають належати автору або бути вільними від авторських прав. (Не забудьте вказати джерело.)

Учасники не повинні використовувати будь-які сервіси ботів для голосування, не брати участь у купівлі голосів.

Порекомендуйте прийняти участь своїм друзям.

Роботи слід опублікувати з Monday 28 Oct 24 to Sunday 3 Nov 24

Ваші роботи будуть мною прокоментовані, оцінені та відібрані чотири кращі роботи.

NЗавданнябали
1Придумайте власний приклад аналогічний до float f=7/2; який ілюструє втрату(спотворення) значення при виконанні ділення. Та покажіть як це виправити. Поясніть чому так відбувається і як ви це виправили?1
2Оберіть самостійно тип даних та проілюструйте обмеженість його діапазону: продемонструйте межу - знизу(перехід через мінімальне значення) і зверху(перехід через максимальне значення. Продемонструйте також перехід через межу при операції множення, поясніть результати.1
3знайдіть свій аналог що 0.1!=0.1, тобто у якісь змінній, знаходиться певне значення, але перевірка показує що там інше число. Або 0.1+0.2!=0.3Чому так?1
4На базі прикладу підрахунку цифр числа напишіть програму що визначає:
(одну на власний вибір):
  • суму цифр числа, (3456 => 3+4+5+6=18), sum(3456) = 18
  • суму цифр числа, але знаходити її і для суми, і для суми від суми, до тих пір доки не одержимо одноцифрову суму - (3456 => 3+4+5+6=18 => 1+8=9). Тут можна зробити безпосередньо так як сказано в завданні, це буде не найкращий варіант, а можна подумати і знайти ефективніше рішення, які я завжди прошу знаходити. sum1(3456)=9
  • знаходить кількість вказаної цифри в числі count_d(143112, 1) = 3, count_d(143112, 5) = 0.
1
5Як підготовку до наступного уроку і повторення минулого, знайдіть найбільше з двох чисел, потім найбільше з трьох чисел(це інше підзавдання), потім найбільше з чотирьох, п'яти, шести, семи - зупинитеся на тому числі як зрозумієте що йдете не раціональною дорогою і для більшої кількосте слід робити інакше. Завдання виконати лише через умовний оператор if()1
6Виконати попереднє завдання через тернарний оператор.1
7Виконати попереднє завдання написавши функцію max() і скориставшись нею.1
8Напишіть функції
  • яка виведе всі дільники вказаного числа print_div(number) - одиницю і саме число number можна не виводити
    print_div(11) => (empty), print_div(12) = > 2 3 4 6
  • функцію sum_div(number) яка обчислить суму своїх дільників, менших його самого
    sum_div(6) = 1+2+3=6, sum_div(10) = 1+2+5=8,
  • функцію яка знайде і надрукує досконалі числа від 1 до n(переданого в функцію числа)
    досконалі числа це такі числа у яких сума дільників, менших самого числа рівна цьому числу
3
матеріал для повторенняㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ‎‎
image.png1. Хто такий програміст? Що треба було робити раніше, щоб потім стати програмістом в майбутньому?
image.png2. How to Prepare Yourself for Programming?
image.png3. Writing the first code
image.png4. Decision Making in Programming: If-Then-Else
image.png5. Iteration in Programming: For, While, and Do-While Loops
image.png6. Practice cycles and Guess the number game

EN

Hello everyone! Congratulations on the opening of the 21st season of the Steemit Learning Challenge.

I invite you to continue learning programming, or to join in if you're new. On one hand, this is a continuation of the previous course in SEC20, so those who participated last time can continue their studies. On the other hand, it can also function as a standalone course, though it’s not recommended for absolute beginners anymore. It requires minimal knowledge but is still suitable for those taking their first steps.

To feel more confident, especially if you’ve just joined, you may review my previous lessons (especially lessons 3-6) and homework assignments. Links are provided in the table at the bottom of the post.

image.png

Let’s delve deeper into C/C++ data types.

As you may already know, languages are categorized as strictly typed and untyped, with static typing or dynamic typing. C/C++ is a language with strict static typing for variables. This means variables must be declared before use, and the type of a variable cannot be redefined later in the code. This leads to some non-obvious "quirks."

Quirk one: 7/2=3, 19/10=1 — this is integer division. The compiler not only computes the value of 7/2, but it also "computes" the type of the result: int/int = int. So, dividing an integer by an integer yields an integer result, nothing unusual — the result is also an integer. Is this a bug, or a feature? Perhaps it’s a bug that became a feature. But it is what it is. Sometimes, this can be useful, and sometimes it needs to be bypassed.

float f=7/2; Here, the issue is that before assigning a value to the variable f, the result is already 3, not 3.5.

In other languages, there are special operations for such integer division.

How can we bypass this? How can we calculate it correctly?

We need to represent the number 7 not as an integer but as a floating-point value — to do this, we write 7.0. So, float f=7.0/2; will yield the expected result.

Let's examine the following code:

int a=7;
int b=2;
float f=a/b;

What to do in this case? Some students suggest writing int a=7.0;, but this won’t work.
Instead, you should write float f=(float)a/b; — this is explicit type casting, where the variable a temporarily changes its type during the calculation. However, the variable itself retains its declared type; the change occurs only at this moment, temporarily, and solely for this expression.

Quirk two

char k=124; — what will k equal if we execute k=k+1;? It seems obvious: 125.
And k++; — clearly, 126.
Then ++k; — surely 127.
And k+=1; — obviously 128, since all these are different ways to increment k by 1.

But the last answer is incorrect. It will not be 128. All data types are limited, meaning their variables have restricted ranges, and the maximum value for char is 127. So what happens if we try to increase it by one more?

Some students answer that, since 127 is the limit, the value will remain 127 after k++, as if the computer refuses to execute the command.

Another example: if the variable unsigned char j=254;, then what will j++ yield? Correct, it will be 255. But what if we apply j++ again? Will it be 256? — It will be 0!

image.png

Imagine a clock showing 55 seconds. What will it display after 7 more seconds pass? 62 seconds — since 55 + 7 = 62, right?
But we know that 62 seconds don't exist. Similarly, in a computer, an unsigned char variable has a range from 0 to 255, so 256 simply cannot physically exist.

I hope it’s easy to guess what will happen if unsigned char j=0; and we execute j--;?)
I demonstrated these quirks with the char and unsigned char data types. The same issues arise with other data types, and even in other languages, with different ranges.

Now, I think it’s clear how 100 x 3 = 44 can happen. After all, 100 x 3 = 300, and 300 - 256 = 44.
But what about 100 x 4? 100 x 4 = 400 (out of range), 400 - 256 = 144 (out of range), 144 - 256 = -112 (within range).

Quirk three

0.1 != 0.1 or 0.1 + 0.2 != 0.3. Remember this! But how is it possible, and why?
There are many reasons. The first is decimal fractions: 1.356, 0.2343, 12.010101... — and also regular fractions or simple division. How do we compute 1/3? It’s 1/3 = 0.3333333333... = 0.(3). This is an infinite, repeating decimal. But computer memory is finite, so we can’t record 1/3 precisely in memory. Actually, we can’t even store 0.1 exactly in computer memory.

It’s worth explaining number systems here. We use the decimal system with ten digits 0123456789, but a computer only has two digits 01. So, our finite 0.1 is actually represented infinitely.
0.1(10) = 0.0100110011001100...(2)

You can investigate this in the following way.

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{    
    cout << setprecision(50) << (float)1/(float)3.0 << endl;
    cout << setprecision(50) << (double)1/(double)3.0 << endl;
    cout << setprecision(50) << (long double)1/(long double)3.0 << endl;
    return 0;
}

Or even simpler:

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{    
    cout << setprecision(50) << 1.f/3.f << endl;
    cout << setprecision(50) << 1./3. << endl;
    cout << setprecision(50) << 1.L/3.L << endl;
    return 0;
}

The value of 50 in setprecision(50) is clearly excessive, as such precision doesn’t exist in C. I intended to highlight the limitations of storing floating-point numbers. To use setprecision(), you need to include the <iomanip> library.

For those writing code in C, this can be done more simply with printf(".50f"); for double and float, and printf(".50Lf"); for long double. Technically, for double, it’s recommended to use printf(".50lf");, although they say the l here has no effect (unlike in scanf).

For float, only 6 digits after the decimal point are accurate (the rest are unreliable “garbage”), for double 15 digits, and for long double it’s 15, 18, or 33 digits — depending on the computer’s architecture.

image.png

Anyone interested can read more details online; the main point here is to remember that numbers are stored with these peculiarities. And that 0.1 + 0.2 != 0.3. So how do we check something like if(a == 0.3)...?

To do this, we need to answer the question: what does "equal" (==) mean? In mathematics, precision is denoted by ε — epsilon. In C/C++, the absolute value of a number is determined using the fabs() function.

Thus, the equality check if(a == 0.3)... would be written as if(fabs(a - 0.3) < 0.0001)..., where 0.0001 is the precision threshold that allows us to consider these numbers equal.

Functions

Let’s consider another point — functions.

Recall the task from the previous lesson that draws a line with *.

int main()
{
    int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

Here, int main() is also a function. In the beginning, it's explained that it’s mandatory and that no program can exist without it, so many write it automatically.

On one hand, this is a program that "draws"/outputs a row of 23 asterisks. But what happens if we rewrite main as line? The program turns into a subroutine.

int line()
{
    int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

But the peculiarity of the C language is that this will no longer work, as main() is missing. So, let's add it back.

int line()
{
    int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

int main()
{
    return 0;
}

After running the program, nothing will happen because simply having the function’s code isn’t enough — we need to call it explicitly.

int line()
{
    int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

int main()
{
    line();
    return 0;
}

However, no matter how many times we call the function, it always outputs 23 asterisks.

If we move int count = 23; to the function declaration line, this function becomes more versatile. It will output as many asterisks as specified in its parameters.

int line(int count)
{
    //int count=23;
    for(int i=1; i<=count; i++)
        cout<<"*";
    cout<<"\n";
    return 0;
}

int main()
{
    line(23);
    line(10);
    return 0;
}

Thus, a function allows you to execute a set of commands with a single call, eliminating the need to rewrite code.

Among data types, there is a special type void. In C/C++, they didn’t differentiate between procedures and functions — everything is called a function. Some functions, however, don’t return anything or, more precisely, they return... nothing. This is what other languages would call a procedure. Therefore, the function int line(int count) isn’t truly a function but rather a procedure. So, it should return no (numerical) value, written as void line(int count).

Here, it would be appropriate to mention formal and actual parameters and how functions receive variable values, not the variables themselves. I’ll skip this for now but will insert it later.

Example 1. Let’s write a function to count the number of digits in a number.

Digit_count(int n)

Example, a simple task: write a function that takes a number as an argument and outputs the number of digits in that number — Digit_count(int n).

Though straightforward, this task is often challenging for beginners.

Obviously, we should declare...

int Digit_count(int n)
{
     
}

image.png

How do you count the digits in a number?

Let’s say the number is n=94665; it’s easy to see there are five digits, but how did I count them? Trying to explain such simple actions to oneself can be challenging, perhaps because it's so easy to count such a small number.

Even if we imagine something tangible, like a box with five apples, it doesn’t get much easier. Let’s then imagine a box full of apples; how would we count them? Eventually, we would come to the idea that we need to move each apple to another box, counting as we go. So the key action here is “reducing by one” as long as there are apples.

Now, let’s apply this analogy to a number. Instead of reducing the number by subtracting 1, we should drop a digit each time, like removing an apple. And how do we remove a single digit from a number? We divide it by 10.

int Digit_count(int n)
{
   int k=0;
   while(n>0)
   {
     n=n/10;
     k++;
   }
   return k;//  cout<<k; 
}

So, as long as there’s still a number, greater than zero (while(n>0)), we keep dividing it by 10, which removes the last digit. Each time, the digit count is reduced by one, so we account for another digit with k++;. This loop will continue until there are no digits left in the number.

Generally, when we write a function for something, it’s best to make a "silent" function — meaning it doesn’t display output (unless specifically required) but returns a result using return. However, here I added cout<<k; as a comment because, during learning or debugging, this kind of output can be helpful.

Example 2

How many six-digit numbers exist in which the digits of the first half are greater than the digits of the second half?

First, some problems can be solved by simple brute-force searching using a computer, especially since we recently covered loops. Let’s set up a loop that checks all six-digit numbers from 100,000 to 999,999 inclusively. Although this is best done with a for loop, like for(int n=100000; n<=999999; n++), I’ll use a while loop here — but in while, I often forget to write n++;((

int n=100000, k=0;
while(n<=999999)
{
    
}

Since we've studied functions, we can write a single universal function that returns a specified digit of a number: f(number, pos); — for instance, f(783091, 2) would return 8. Alternatively, we could write six separate functions, each for a specific digit in a six-digit number: f1(number), f2(number), ...f6(number). For this particular task, and for educational purposes, I actually prefer the second approach.

This way, each function isolates the extraction of one specific digit, which can simplify understanding for beginners by breaking the task into smaller, more focused operations. This approach also reinforces the concept of functions as modular units in a program.

inline int f1(int number){ return number%1000000/100000; }
inline int f2(int number){ return number%100000/10000; }
inline int f3(int number){ return number%10000/1000; }
inline int f4(int number){ return number%1000/100; }
inline int f5(int number){ return number%100/10; }
inline int f6(int number){ return number%10/1; }

Here, I declared the functions as inline, which is a suggestion to the compiler to insert the function's code directly into the program's code. Thus, the function is only formal in nature. However, if the programmer had inserted the body of the function themselves, it would have complicated the readability of the code.

Currently, declaring inline functions is not mandatory; the compiler often decides on its own. It may not make complex functions inline, while it could automatically treat very simple functions as inline without explicit instruction. An essential rule for homework is to declare one of the functions in the assignment as inline, any function will do.

if(  f1(n)>f4(n) && f1(n)>f5(n) && f1(n)>f6(n) &&
     f2(n)>f4(n) && f2(n)>f5(n) && f2(n)>f6(n) &&
     f3(n)>f4(n) && f3(n)>f5(n) && f3(n)>f6(n) )
     k++;

Homework

NTaskPoints
1Come up with your own example similar to float f=7/2; that illustrates the loss (distortion) of value during division. Show how to fix it. Explain why this happens and how you fixed it.1
2Choose a data type independently and illustrate the limitations of its range: demonstrate the lower boundary (crossing the minimum value) and the upper boundary (crossing the maximum value). Also, demonstrate crossing the boundary during multiplication, explaining the results.1
3Find your own example of 0.1!=0.1, meaning there is a certain value in a variable, but the check shows a different number. Or 0.1+0.2!=0.3. Why is this happening?1
4Based on the example of counting digits in a number, write a program that determines:
(one of your choice):
  • the sum of the digits of a number, (3456 => 3+4+5+6=18), sum(3456) = 18
  • the sum of the digits of a number, but finding it for the sum, and for the sum of the sum, until you get a one-digit sum - (3456 => 3+4+5+6=18 => 1+8=9). Here you can do it directly as stated in the task, it won't be the best option, but you can think and find a more efficient solution, which I always ask to find. sum1(3456)=9
  • find the number of a specified digit in a number count_d(143112, 1) = 3, count_d(143112, 5) = 0.
1
5As preparation for the next lesson and to review the past, find the largest of two numbers, then the largest of three numbers (this is a different subtask), then the largest of four, five, six, seven - stop when you understand that you are not following a rational path and for larger quantities you should do it differently. Complete the task only using the conditional operator if().1
6Perform the previous task using the ternary operator.1
7Perform the previous task by writing a function max() and using it.1
8Write functions:
  • that will print all the divisors of a given number print_div(number) - you can skip printing one and the number itself
    print_div(11) => (empty), print_div(12) => 2 3 4 6
  • a function sum_div(number) that will calculate the sum of its divisors that are less than itself
    sum_div(6) = 1+2+3=6, sum_div(10) = 1+2+5=8,
  • a function that will find and print perfect numbers from 1 to n (the number passed to the function)
    perfect numbers are those where the sum of divisors less than the number equals the number itself.
3

Contest Guidelines

You can publish in any language, in any community, or just on your own blog; add a link to your work in the comments here.

To help me quickly find, check, and assess your work, please leave the link in a comment under this text, and in your work, use the tag #slc21w1sergeyk.

For all tasks, provide the code as a screenshot, not as text. Also, demonstrate the program's results with a screenshot.

Be cautious regarding ideal and overly efficient solutions; they are not easy for a regular beginner to find.

Do not provide solutions using materials that have not been covered. For example, arrays, which we have not studied yet. This restriction does not apply to students who are already practically familiar with programming and provide extended answers to tasks that resemble a lecture.

Plagiarism and the use of AI are prohibited.

Participants must be verified and active users of the platform.

Used images must belong to the author or be free of copyright. (Don't forget to cite the source.)

Participants should not use any bot voting services or engage in vote buying.

Feel free to recommend your friends to participate.

Submit your work from Monday, October 28, 2024, to Sunday, November 3, 2024.

Your work will be commented on, assessed, and the four best works will be selected by me.

material for reviewㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ‎‎
image.png1. Who is a programmer? What should have been done earlier to become a programmer in the future?
image.png2. How to Prepare Yourself for Programming?
image.png3. Writing the first code
image.png4. Decision Making in Programming: If-Then-Else
image.png5. Iteration in Programming: For, While, and Do-While Loops
image.png6. Practice cycles and Guess the number game