LINUX.ORG.RU

Сообщения KOMMUNIST90

 

Красно-чёрное дерево. Потеря связности блоков памяти

Пишу красно-чёрное дерево на Си.

В данный момент в коде уже реализованы функции перекрашивания recolor, а также левого и правого поворотов с перекрашиванием left_rotation_and_recolor и right_rotation_and_recolor, что позволяет обрабатывать последовательности чисел отсортированных как по возрастанию, так и по убыванию.

Теперь пришла пора реализовать функции left_rotation и right_rotation, чтобы можно было выполнить вставку узла в ситуации, подобной приведённой ниже (применив сначала перекрашивание, затем левый поворот и затем правый поворот с перекрашиванием):

        50(b)                       50(b)                       50(b)                           30(b)
        /  \                        /  \                        /  \                            /  \
    20(r)   60(b)               20(r)   60(b)               30(r)   60(b)                   20(r)   50(r)
    /  \       \                /  \       \                /  \       \                /       \   /       \
10(b)   30(b)   70(r)   =>  10(b)   30(r)   70(r)   =>  20(r)   40(b)   70(r)   =>  10(b)   25(b)   40(b)   60(b)
        /  \                        /  \                /  \                                /                  \
    25(r)   40(r)               25(b)   40(b)       10(b)   25(b)                       22(r)                   70(r)
    /                           /                           /
22(r)                       22(r)                       22(r)

Однако, в процессе этого в функции left_rotation

void left_rotation(node **p)
{
    node *left_son = LEFT_SON_VAR_PAR; /* (*p)->left */
    node *dad = DAD_VAR_PAR;
    node *grandpa = GRANDPA_VAR_PAR;
    grandpa->left = *p;
    (*p)->prev = grandpa;
    dad->prev = *p;
    dad->right = left_son;
    if (left_son)
        left_son->prev = dad;
    (*p)->left = dad;
}

после исполнения кода в строке left_son->prev = dad; одномоментно теряется связность сразу множества блоков памяти и дерево «перекорёживается».

Это хорошо видно при отладке в GDB:

215	        left_son->prev = dad;
5: left_son->value = 25
6: dad->value = 20
7: grandpa->value = 50
8: (*p)->prev->value = 50
9: (*p)->right->value = 40
10: (*p)->left->left->value = 22
11: (*p)->value = 30
(gdb) n
216	    (*p)->left = dad;
5: left_son->value = 25
6: dad->value = 20
7: grandpa->value = 50
8: (*p)->prev->value = 30
9: (*p)->right->value = 25
10: (*p)->left->left->value = <error: Cannot access memory at address 0x18>
11: (*p)->value = 20

Никак не могу взять в толк, с чем это связано. Может кто-то популярно объяснить?

Полный текст программы:

/* red-black_tree.c */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>

#define GREATGRANDPA_VAR_PAR (*p)->prev->prev->prev
#define GRANDPA_VAR_PAR (*p)->prev->prev
#define RIGHT_UNCLE_VAR_PAR (*p)->prev->prev->right
#define LEFT_UNCLE_VAR_PAR (*p)->prev->prev->left
#define RIGHT_UNCLE p->prev->prev->right
#define LEFT_UNCLE p->prev->prev->left
#define DAD_VAR_PAR (*p)->prev
#define DAD p->prev
#define RIGHT_BRO_VAR_PAR (*p)->prev->right
#define LEFT_BRO_VAR_PAR (*p)->prev->left
#define RIGHT_BRO p->prev->right
#define LEFT_BRO p->prev->left
#define LEFT_SON_VAR_PAR (*p)->left

typedef enum tag_node_color { red, black } node_color;

typedef struct tag_node {
    struct tag_node *left, *right, *prev;
    int value;
    node_color color;
} node;

node **find_node(int num, node **p, node **previous)
{
    if ((!*p) || ((*p)->value == num))
        return p;
    *previous = *p;
    if (num < (*p)->value)
        return find_node(num, &(*p)->left, previous);
    else
        return find_node(num, &(*p)->right, previous);
}

int tree_height(node *p)
{
    int curr_height = 0;
    if (!p)
        return 0;
    if (tree_height(p->left) >= tree_height(p->right))
        curr_height = tree_height(p->left);
    else
        curr_height = tree_height(p->right);
    return curr_height + 1;
}

node **current_root_is(node **p)
{
    return (!DAD_VAR_PAR) ? p : current_root_is(&DAD_VAR_PAR);
}

bool grandpa_is_right_son(node *p)
{
    return (p->value == RIGHT_BRO->value) ? true : false;
}

void left_rotation_and_recolor(node **p)
{
    node *dad = DAD_VAR_PAR;
    node *left_bro = LEFT_BRO_VAR_PAR;
    node *grandpa = GRANDPA_VAR_PAR;
    node *great_grandpa = GREATGRANDPA_VAR_PAR;
    if (great_grandpa) {
        if (grandpa_is_right_son(grandpa))
            great_grandpa->right = dad;
        else
            great_grandpa->left = dad;
    }
    if (left_bro)
        left_bro->prev = grandpa;
    grandpa->right = left_bro;
    dad->left = grandpa;
    dad->prev = great_grandpa;
    grandpa->prev = dad;
    dad->color = black;
    grandpa->color = red;
}

void right_rotation_and_recolor(node **p)
{
    node *dad = DAD_VAR_PAR;
    node *right_bro = RIGHT_BRO_VAR_PAR;
    node *grandpa = GRANDPA_VAR_PAR;
    node *great_grandpa = GREATGRANDPA_VAR_PAR;
    if (great_grandpa) {
        if (grandpa_is_right_son(grandpa))
            great_grandpa->right = dad;
        else
            great_grandpa->left = dad;
    }
    if (right_bro)
        right_bro->prev = grandpa;
    grandpa->left = right_bro;
    dad->right = grandpa;
    dad->prev = great_grandpa;
    grandpa->prev = dad;
    dad->color = black;
    grandpa->color = red;
}

bool dad_is_left_son(node *p)
{
    if (!LEFT_UNCLE)
        return false;
    if (LEFT_UNCLE->value == DAD->value)
        return true;
    else
        return false;
}

bool dad_is_right_son(node *p)
{
    if (!RIGHT_UNCLE)
        return false;
    if (RIGHT_UNCLE->value == DAD->value)
        return true;
    else
        return false;
}

bool im_left_son(node *p)
{
    if (!LEFT_BRO)
        return false;
    if (LEFT_BRO->value == p->value)
        return true;
    else
        return false;
}

bool im_right_son(node *p)
{
    if (!RIGHT_BRO)
        return false;
    if (RIGHT_BRO->value == p->value)
        return true;
    else
        return false;
}

bool uncle_is_black(node *p)                        /* technically I check */
{                                       /* if both parent and uncle are black */
    if ((!LEFT_UNCLE) || (!RIGHT_UNCLE))
        return true;
    if ((LEFT_UNCLE->color == black) || (RIGHT_UNCLE->color == black))
        return true;
    else
        return false;
}

void set_grandpa_color_red(node **p)
{
    if (!GREATGRANDPA_VAR_PAR)
        return;
    else
        GRANDPA_VAR_PAR->color = red;
}

void set_dad_and_uncle_colors_black(node **p)
{
    if ((!LEFT_UNCLE_VAR_PAR) || (!RIGHT_UNCLE_VAR_PAR))
        return;
    LEFT_UNCLE_VAR_PAR->color = black;
    RIGHT_UNCLE_VAR_PAR->color = black;
}

void self_balance(node **p);

void recolor(node **p)
{
    set_dad_and_uncle_colors_black(p);
    set_grandpa_color_red(p);
    self_balance(&GRANDPA_VAR_PAR);
}

bool uncle_is_red(node *p)                          /* technically I check */
{                                       /* if both parent and uncle are red */
    if ((!LEFT_UNCLE) || (!RIGHT_UNCLE))
        return false;
    if ((LEFT_UNCLE->color == red) &&
        (RIGHT_UNCLE->color == red))
    {
        return true;
    } else {
        return false;
    }
}

bool dad_is_red(node *p)
{
    return (DAD->color == red) ? true : false;
}

bool dad_is_black(node *p)
{
    return (DAD->color == black) ? true : false;
}

void left_rotation(node **p)
{
    node *left_son = LEFT_SON_VAR_PAR; /* (*p)->left */
    node *dad = DAD_VAR_PAR;
    node *grandpa = GRANDPA_VAR_PAR;
    grandpa->left = *p;
    (*p)->prev = grandpa;
    dad->prev = *p;
    dad->right = left_son;
    if (left_son)
        left_son->prev = dad;
    (*p)->left = dad;
}

void self_balance(node **p)
{
    if ((!DAD_VAR_PAR) || (dad_is_black(*p)) || ((*p)->color == black))
        return;
    if (dad_is_red(*p)) {
        if (uncle_is_red(*p)) {
            recolor(p);
            self_balance(&GRANDPA_VAR_PAR);
        } else
        if (uncle_is_black(*p) && im_right_son(*p) && dad_is_right_son(*p)) {
            left_rotation_and_recolor(p);
            self_balance(&DAD_VAR_PAR);
        } else
        if (uncle_is_black(*p) && im_left_son(*p) && dad_is_left_son(*p)) {
            right_rotation_and_recolor(p);
            self_balance(&DAD_VAR_PAR);
        } else
        if (uncle_is_black(*p) && im_right_son(*p) && dad_is_left_son(*p)) {
            left_rotation(p);
            right_rotation_and_recolor(&(*p)->left);
            self_balance(p);
        }
    }
}

void add_node(node **root, int num, int *height)
{
    node **position = NULL;
    node *previous = NULL;
    position = find_node(num, root, &previous);
    if (*position != NULL)
        printf("The node %d already exists.\n", num);
    else {
        *position = malloc(sizeof(node));
        (*position)->value = num;
        (*position)->right = NULL;
        (*position)->left = NULL;
        (*position)->prev = previous;
        if ((*position)->prev) {
            (*position)->color = red;
            self_balance(position);
            position = current_root_is(position);   /* check if root was */
            *root = *position;                  /* changed and set new root */
        } else
            (*position)->color = black;         /* it's the very 1st node */
        *height = tree_height(*root);
    }
}

void print_color_and_dad_value(node *p)
{
    if (p->color == black)
        printf(",blk(");
    else
        printf(",red(");
    if (p->prev == NULL)
        printf(" ) ");
    else
        printf("%d) ", p->prev->value);
}

void print_tree_level(node *p, int height, int curr_height)
{
    if (!p)
        return;
    if (height == curr_height) {
        printf("%d", p->value);
        print_color_and_dad_value(p);
    } else {
        print_tree_level(p->left, height, curr_height+1);
        print_tree_level(p->right, height, curr_height+1);
    }
}

void print_tree(node *p, int height)
{
    int i;
    for (i=1; i <= height; i++) {
        print_tree_level(p, i, 1);
        putchar('\n');
    }
}

void free_tree(node *p)
{
    if (!p)
        return;
    free_tree(p->left);
    free_tree(p->right);
    free(p);
}

int main()
{
    node *root = NULL;
    int i, height = 0;
    int num[] = { 50, 20, 60, 70, 10, 30, 25, 40, 22 };
    for (i=0; i < (int)(sizeof(num)/sizeof(num[0])); i++)
        add_node(&root, num[i], &height);
    putchar('\n');
    print_tree(root, height);
    free_tree(root);
    return 0;
}

 , ,

KOMMUNIST90
()

Корректен ли мой вариант решения задачи (поиск подстроки в строке)?

Решил задчку 4.12 из «Программирование: введение в профессию. Задачи и этюды» А.В. Столярова:

"Напишите функцию, которая в заданной строке находит вхождение заданной подстроки и, если таковое обнаружено, возвращает адрес (не индекс в массиве, а именно адрес!) начала подстроки в строке, если же заданная подстрока не обнаружена - возвращает NULL. Используя эту функцию, напишите программу, которая принимает на вход произвольное (не менее одного) количество аргументов командной строки, первый из них рассматривает как подстроку, и печатает:

a) те из оставшихся аргументов, которые содержат в себе эту подстроку (каждый на отдельной строке);

b) для каждого из аргументов, содержащих в себе подстроку - сам этот аргумент и количество вхождений в него указанной подстроки.

Обратите внимание, что написанной вами функции, если она соответствует спецификации, достаточно для решения обоих пунктов задачи, в том смысле что никакой дополнительный анализ строк здесь не нужен, можно вообще не обращаться к элементам строк ни напрямую, ни как-то ещё, кроме как через написанную функцию."

Программа получилась следующей:

/* num_of_pat_occur_in_cmd_line_args.c */

#include <stdio.h>
#include <string.h>

enum boolean { false, true };

enum boolean fit(const char *str, const char *pat)
{
    for (; *pat; str++, pat++) {
        if (*str != *pat)
            return false;
    }
    return true;
}

const char *addr_of_occur(const char *str, const char *pat)
{
    for (; *str; str++) { 
        if (*str == *pat) {         /* start of pattern occurence */
            if (fit(str, pat))      /* checking if the rest of the pattern */
                return str;         /* match */
        }
    }
    return NULL;
}

int num_of_pat_occur(const char *str, const char *pat)
{
    int n = 0;
    while ((str = addr_of_occur(str, pat))) {
        str = str + strlen(pat);
        n++;
    }
    return n;
}

int main(int argc, char **argv)
{
    char *pat;
    int i;
    if (argc == 0) {
        printf(
            "Please provide command line arguments: pattern and string(s)\n"
        );
        return 1;
    }
    pat = argv[1];
    for (i=2; i < argc; i++) {
        int n = 0;
        n = num_of_pat_occur(argv[i], pat);
        if (n > 0)
            printf("%s - %d;\n", argv[i], n);
    }
    return 0;
}

Однако меня смущает тот факт, что в функции num_of_pat_occur я использовал strlen, хотя автор говорит что «можно вообще не обращаться к элементам строк ни напрямую, ни как-то ещё, кроме как через написанную (вами) функцию».

Возможно я что-то упустил?

 , ,

KOMMUNIST90
()

Проблема с работой клиента Outline на Linux

Давно использую приложение Outline на смартфоне. Решил установить клиент и на ноутбук (Ubuntu 20.04.3). Скачал Outline-Client.AppImage (отсюда - https://getoutline.org/ru/get-started/#step-3) Переместил в /usr/local/bin. Выдал разрешение на запуск на исполнение:

chmod u+x Outline-Client.AppImage

Запустил клиент. Добавил ключ для соединения с сервером. Жму «подключить». Выскакивает окошко «Для запуска Outline могут потребоваться права администратора. Продолжить? Ok Отмена» Нажимаю Ok - «Не удалось запустить Outline. Отправьте отзыв через приложение.»

Решаю запустить с правами администратора:

sudo Outline-Client.AppImage
[0407/174828.653353:FATAL:electron_main_delegate.cc(292)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.
Ловушка трассировки/останова

Запускаю с ключом –no-sandbox:

sudo Outline-Client.AppImage --no-sandbox

В результате открывается пустое окошко клиента без всякого содержимого.

Может какой добрый человек поможет справиться с проблемой?

 , , ,

KOMMUNIST90
()

Десятичное представление числа, не помещающегося в один регистр, в ассемблере NASM

Добрый день!

Если у нас есть число, сохранённое в регистре, и нам нужно получить значения разрядов его десятичного представления - то всё довольно просто: только то и нужно, что делить его на 10 и сохранять полученные остатки.

lp:     cmp     eax, 0  ; have we finished?
        je      prinrt
        inc     ecx
        div     dword [divider]
        mov     [result + ecx - 1], dl
        xor     edx, edx
        jmp     lp

А как поступить, если число не помещается в одном регистре? Допустим, была выполнена команда mul ebx и итоговое число слишком большое, чтобы поместиться в 32 бита, и теперь хранится в edx:eax?

 ,

KOMMUNIST90
()

Компоновщик ассемблера ругается

Доброго вечера!

Потихоньку решаю ассемблерную часть из задачника А.В. Столярова. По итогу решения задачки 3.20 «Получи на вводе два числа. Выведи их сумму, разность и произведение» родил данный код:

%include"stud_io.inc"
global  _start

section .bss
FstArg  resd 1
SndArg  resd 1
string  resb 10
summ    resd 10
diff    resd 10
prod    resd 20

section .data
ErrMsg  db "There must be two numbers divided with one space symbol"
factor  db 10
divider dw 10

section .text
_start  xor     eax, eax; prepare char buffer
        xor     ebx, ebx; prepare current number holder (arg calculation)
        xor     ecx, ecx; prepare number counter
        xor     edx, edx; prepare data register
        mov     dword [FstArg], 0
                        ; prepare 1st argument
        mov     dword [SndArg], 0
                        ; prepare 2nd argument
NRead:  GETCHAR         ; read a char
        cmp     eax, ' '; do we have space character? 
        je      ZChck   ; then check - has anything already been printed?       
        jmp     NxtStg  ; if we dont have space char - continue alalyz char
ZChck:  cmp     ecx, 0  ; has anything already been printed?
        je      ErrPrt  ; if not then print error message
        jmp     ArgPr1  ; prepare 1st argument
NxtStg: cmp     eax, '0'; if we have ascii symbol less than '0' 
        jb      ErrPrt  ; then print error message
        cmp     eax, '9'; if we have ascii symbol more than '9'
        ja      ErrPrt  ; then print error message 
        sub     eax, '0'; convert ascii code into a number
        inc     ecx     ; increase number counter       
        mov     [string + ecx - 1], al
                        ; write another number into array
        jmp     NRead   ;
ArgPr1  xor     eax, eax; prepare the accamulator
lp:     mov     ebx, [string + ecx - 1]
                        ; get the current number from the array
        mul     byte [factor]
                        ; multiplying accumulator value by 10
        add     eax, ebx; adding to accumulator current number
        loop    lp
        add     dword [FstArg], eax
                        ; we got complete 1st argument now
        mov     ecx, 10 ; prepare counter for string cleanup 
StrClUp:mov     byte [string + ecx - 1], 0
                        ; set current byte to zero
        loop    StrClUp
        xor     eax, eax; prepare char buffer
        xor     ebx, ebx; prepare current number holder (arg calculation)
        xor     ecx, ecx; prepare the counter
NRead2  :GETCHAR        ; read a char
        cmp     eax, 10 ; do we have break line character? 
        je      ZChck2  ; then check - has anything already been printed?       
        jmp     NxtStg2 ; if we dont have space char - continue alalyz char
ZChck2: cmp     ecx, 0   ; has anything already been printed?
        je      ErrPrt  ; if not then print error message
        jmp     ArgPr2  ; prepare 2nd argument
NxtStg2:cmp     eax, '0'; if we have ascii symbol less than '0' 
        jb      ErrPrt  ; then print error message
        cmp     eax, '9'; if we have ascii symbol more than '9'
        ja      ErrPrt  ; then print error message 
        sub     eax, '0'; convert ascii code into a number
        inc     ecx     ; increase number counter       
        mov     [string + ecx - 1], al
                        ; write another number into array
        jmp     NRead2  ;
ErrPrt: PRINT   ErrMsg
ArgPr2: xor     eax, eax; prepare the accamulator
lp2:    mov     ebx, [string + ecx - 1]
                        ; get the current number from the array
        mul     byte [factor]
                        ; multiplying accumulator value by 10
        add     eax, ebx; adding to accumulator current number
        loop    lp2
        add     dword [SndArg], eax
                        ; we got complete 2nd argument now
        PRINT   "SUM"   ; <<<< sum opperation
        PUTCHAR 10
        add     eax, [FstArg]
lp3:    cmp     eax, 0  ; have we finished?
        je      SumPrt
        div     dword [divider]
        inc     ecx
        mov     [summ + ecx - 1], edx
        jmp     lp3
SumPrt: xor     ebx, ebx; PRINT SUM     
lp4:    mov     ebx, [string + ecx - 1]
                        ; copying a number from string array
        add     ebx,'0' ; converting a number from binary to ascii format
        PUTCHAR bl      ; print numbers of the string array
        loop    lp4     ; one by one
        PUTCHAR 10
        xor     eax, eax
        xor     ebx, ebx
        xor     ecx, ecx
        xor     edx, edx
        PRINT   "DIFF"  ; <<<< sub operation
        PUTCHAR 10
        mov     eax, [FstArg]
        mov     ebx, [SndArg]
        sub     eax, ebx; do we have negative difference?
        jns     lp4     ; if not - proceed with the standart algoritm
        not     eax     ; else take additional actions - invert
        add     eax, 1  ; and add 1 - so we convert neg num into positiv one
        PUTCHAR "-"
lp5:    cmp     eax, 0  ; (have we finished?) if no start work with pos num
        je      SubPrt
        div     dword [divider]
        inc     ecx
        mov     [diff + ecx - 1], edx
        jmp     lp5
SubPrt: xor     ebx, ebx; PRINT SUB
lp6:    mov     ebx, [diff + ecx - 1]
                        ; copying a number from string array
        add     ebx, '0'; converting a number from binary to ascii format
        PUTCHAR bl      ; print numbers of the string array
        loop    lp6     ; one by one
        PUTCHAR 10
        xor     eax, eax
        xor     ebx, ebx
        xor     ecx, ecx
        xor     edx, edx
        PRINT   "PROD"  ; <<<< mul operation
        PUTCHAR 10
        mov     eax, [FstArg]
        mov     ebx, [SndArg]
        mul     ebx
        mov     ecx, 20 
lp7:    cmp     eax, 0  ; (have we finished?) if no start work with pos num
        je      MulPrt
        div     dword [divider]
        dec     ecx
        mov     [prod + ecx], edx
        jmp     lp7
        cmp     edx, 0  ; does the second multiplyer register is empty?
        je      MulPrt  ; then just print the number
        mov     eax, edx; else start ro work with it too
lp8:    cmp     eax, 0  ; (have we finished?) if no start work with pos num
        je      MulPrt
        div     dword [divider]
        dec     ecx
        mov     [prod + ecx], edx
        jmp     lp8
MulPrt: nop
lp9:    mov     ebx, [prod + ecx]
                        ; copying a number from string array
        inc     ecx
        add     ebx, '0'; converting a number from binary to ascii format
        PUTCHAR bl      ; print numbers of the string array
        loop    lp9     ; one by one
        PUTCHAR 10

При запуске компоновщика получаю данный поток нецензурной брани в свой адрес:

~/assembly$ ld -m elf_i386 TwoNumSumDifMul.o -o TwoNumSumDifMul
TwoNumSumDifMul.o: в функции «..@10.str»:
TwoNumSumDifMul.asm:(.text+0xfc): перемещение обрезано по месту: R_386_8 у неопределённого символа «.data»

Перемещено hobbit из general

 ,

KOMMUNIST90
()

Ошибки в хэлловорлде на ассемблере NASM

Добрый день!

Прохожу материал из книжки А.В. Столярова «Программирование. Введение в профессию». Начал часть, посвящённую ассемблеру. Прочитал «3.2. Основы системы комманд i386». Решил на память написать программу, которая пять раз печатает «Hello». Получилось следующее:

%include "stud_io.inc"
global   _start

section .text
_start: mov     eax, 5
rep:    cmp     eax, 0
        je      quit
        PRINT   "Hello"
        PUTCHAR 10
        dec     eax
        jmp     rep
quit:   FINISH

После команды оттранслировать файл с текстом программы в объектный файл

nasm -f elf hello5rep.asm

получил следующие ошибки:

hello5rep.asm:6: error: parser: instruction expected
hello5rep.asm:11: error: expression syntax error

У автора программа выглядит следующим образом:

%include "stud_io.inc"
global  _start

section .text
_start: mov     eax, 0
repeat: PRINT   "Hello"
        PUTCHAR 10
        inc     eax
        cmp     eax, 5
        jl      repeat
        FINISH

Конечно, по памяти переписал я её менее изящно, но причины возникающих ошибок всё равно не пойму.

ЧЯДНТ?

 ,

KOMMUNIST90
()

Оператор read в Pascal отказывается читать пробельные символы

Добрый день!

Решаю задачи из книжки А.В. Столярова «Программирование. Введение в профессию. Задачи и этюды». Там, в частности, есть задача 2.42:

«Напишите программу, которая читает из стандартного потока ввода строки, состоящие из слов (слова могут разделяться произвольными группами пробельных символов), и в ответ на каждую прочитанную строку печатает слова из этой строки в обратном порядке; например, в ответ на фразу «Humpty Dumpty sat on a wall» должно быть напечатано «wall a on sat Dumpty Humpty». Вводить ограничния на длины строк, слов и др. нельзя; в частности, недопустимо использовать тип string для хранения читаемых строк и/или отдельных слов. Вся выделенная динамическая память должна быть корректно освобождена сразу после обработки очередной строки.»

Я написал текст программы:

program ReverseStringsOfWords;

type
    ListOfChrPtr = ^ListOfChr;                  {list of chars}
    ListOfChr = record
        ch: char;
        next: ListOfChrPtr;
        end;

    ListOfWrdPtr = ^ListOfWrd;                  {list of words}
    ListOfWrd = record
        wr: ListOfChrPtr;
        next: ListOfWrdPtr;
        end;

procedure AddWord(var first: ListOfWrdPtr; var LOCfirst: ListOfChrPtr);
var
    tmp: ListOfWrdPtr;
begin
    new(tmp);
    tmp^.wr := nil;
    LOCfirst := tmp^.wr;
    tmp^.next := first;
    first := tmp;
end;

procedure AddToWord(
    var first, last: ListOfChrPtr; var LOW: ListOfWrdPtr; c: char
    );
begin
    if first = nil then
        begin
        new(LOW^.wr);
        last := LOW^.wr;
        first := last;
        end
    else
        begin
        new(last^.next);
        last := last^.next;
        end;
    last^.ch := c;
    last^.next := nil;
end;

procedure TypeAWord(first: ListOfChrPtr);
begin
    while first <> nil do
        begin
        write(first^.ch);
        first := first^.next;
        end;
end;

procedure TypeWordsInReverseOrder(first: ListOfWrdPtr);
begin
    while first <> nil do
        begin
        TypeAWord(first^.wr);
        write(' ');
        first := first^.next;
        end;
end;

var
    c: char;
    SpcPressed: boolean;
    LOW: ListOfWrdPtr;
    LOCfirst, LOClast: ListOfChrPtr;
begin
    while not SeekEof do
        begin
        LOW := nil;
        LOCfirst := nil;
        LOClast := nil;
        SpcPressed := true;
        while not SeekEoln do
            begin
            read(c);
            if (SpcPressed = true) and (c <> ' ') and (c <> #9) then
                begin
                AddWord(LOW, LOCfirst);
                AddToWord(LOCfirst, LOClast, LOW, c);
                {just create a new word}
                {start filling in a new word}
                SpcPressed := false;
                end
            else
            if (c = ' ') or (c = #9) then
                begin
                {end of word}
                SpcPressed := true;
                end
            else
            if (SpcPressed = false) and (c <> ' ') and (c <> #9) then
                begin
                AddToWord(LOCfirst, LOClast, LOW, c);
                {continuing typing a word (adding elements to old list)}
                end;
            end;
        TypeWordsInReverseOrder(LOW);
        writeln;
        {cleaning up heap}
        end;
end.

Но по какой-то причине программа «не видит» вводимые пробельные символы. Например, если напечатать «pi po» выводом будет «pipo».

Что я упустил?

 , ,

KOMMUNIST90
()

Я неправильно понимаю булеву логику в Pascal?

Добрый день! Написал свою версию программы из учебника А.В. Столярова «Азы программирования» в которой можно передвигать звездочку по экрану терминала (2-е издание, глава 2.8.3 стр. 374).

program WonderingStar;
uses crt;
const
    message = '*';
    DelayDuration = 100;            {0.1 sec}

procedure GetKey(var code: integer);
var
    c: char;
begin
    c := ReadKey;
    if c = #0 then
        begin
        c := ReadKey;
        code := -ord(c)
        end
    else
        code := ord(c)
end;

procedure ShowMessage(x, y: integer; msg: string);
begin
    GotoXY(x, y);
    write(msg);
    GotoXY(1, 1)
end;

procedure ClearMessage(x, y: integer; msg: string);
var
    len, i: integer;
begin
    len := length(msg);
    GotoXY(x, y);
    for i := 1 to len do
        write(' ');
    GotoXY(1,1)
end;

procedure ScreenBoundariesCheck(var x, y: integer; msg: string; dx, dy: integer);
begin
    if (x + dx >= 1) and (x + dx <= ScreenWidth) then
        x := x + dx;
    if (y + dy >= 1) and (y + dy <= ScreenHeight) then
        y := y + dy
end;

procedure MoveMessage(var x, y: integer; msg: string; dx, dy: integer);
begin
    while true do
        begin
        ClearMessage(x, y, msg);
        ScreenBoundariesCheck(x, y, msg, dx, dy);
        if not (x = ScreenWidth) and not (y = ScreenHeight) then
            ShowMessage(x, y, msg);
        delay(DelayDuration);
        if KeyPressed then
            exit
        end
end;

var
    CurX, CurY, i: integer;
begin
    clrscr;
    CurX := (ScreenWidth - length(Message)) div 2;
    CurY := ScreenHeight div 2;
    ShowMessage(CurX, CurY, Message);
    while true do
        begin
        GetKey(i);
        case i of
            -72:        {down key}
                MoveMessage(CurX, CurY, Message, 0, -1);
            -80:        {up key}
                MoveMessage(CurX, CurY, Message, 0, 1);
            -75:        {left key}
                MoveMessage(CurX, CurY, Message, -1, 0);
            -77:        {right key}
                MoveMessage(CurX, CurY, Message, 1, 0);
            32:         {space key}
                MoveMessage(CurX, CurY, Message, 0, 0);
            27:         {finish (escape key)}
                break
            end
        end;
    clrscr
end.

В процедуре MoveMessage фрагмент на строке 53

    if not (x = ScreenWidth) and not (y = ScreenHeight) then
        ShowMessage(x, y, msg);

отвечает за то, чтобы в последней позиции последней строки ничего нельзя было напечатать, иначе из-за особенностей модуля crt будет происходить скроллинг всего экрана.

т.е., насколько я понимаю, я написал: «Если одновременно и X и Y принимают максимальное значение, то звёздочку не печатаем».

Однако после компиляции и запуска программы я обнаружил, что звёздочка не печатается во всей последней строке и во всем посдеднем столбце.

В то же время я обнаружил, что если в условии поменять and на or, т.е. написать «Если у нас либо X либо Y принимает максимальное значение, то звёздочку не печатаем», то всё заработает именно так, как я хочу: не будет печататься «нижний правый пиксель».

т.е. по факту булева логика в этом примере работает с точностью наоборот, от того, что я ожидаю.

В чём я ошибаюсь?

 ,

KOMMUNIST90
()

Вопрос по Bourne Shell

В учебнике А.В. Столярова «Азы программирования» в главе 2.13.2 приводится пример небольшого теста, написанного на языке Bourne Shell, который должен помочь в тестировании написанных программ.

У меня это свой вариант процедуры str на Pascal, которая переводит число в строковое представление и при вводе str(321.53459:15:9, s) занесет в переменную s строку " 321.534590000" длиной 15 символов с 9-ю символами после запятой и 2-мя пробелами вначале.

Я переписал под себя тестовую программу:

#1/bin/sh
#               RealToStringEdit_Test.sh

while read a b c d ; do
    res=`echo $a $b $c | ./RealToStringEdit`
    if [ x"$d" != x"$res" ]; then
        echo TEST $a $b $c FAILED: expected "$d", got "$res"
    else
        echo CONGRATULATIONS!
    fi
done <<END
321.53459 15 9 '  321.534590000'
END

Вопрос - как занести в переменную $d строку с пробелами в начале, как в моем случае? У меня строка заносится с кавычками, а мне нужно без.

Вопрос в догонку - для чего ставятся x перед переменными в условии?

 , ,

KOMMUNIST90
()

Как устранить влияние погрешности при вычислениях с использованием типа Real в Pascal?

Решил написать на Pascal свой вариант процедуры str, которая переводит число в строковое представление и при вводе str(12.5:9:3, s) занесет в переменную s строку " 12.500" длиной 9 символов с 3-мя символами после запятой и 3-мя пробелами вначале.

Так вот, при написании процедуры, ответственной за перевод в строковое представление части числа после запятой, возникла проблема.

program Example;

function
    FracPart(n: real): real;
begin
    if n - round(n) >= 0 then
        FracPart := n - round(n)
    else
        FracPart := 1 + n - round(n)
end;

function
    IntPart(n:real): integer;
begin
    if n - round(n) >= 0 then
        IntPart := round(n)
    else
        IntPart := round(n) - 1
end;

function
    IntToChr(n: shortint): char;
begin
    IntToChr := chr(n + 48)
end;

var
    n: real;
    b,i: byte;
    s2: string;
begin
    writeln('Enter a number');
    readln(n);
    writeln('Enter a number of digits after the point');
    readln(b);
    n := FracPart(n);
    s2 := '';
    if n = 0 then
    begin
        if b > 0 then
        begin
            s2 := '.';
            for i := 1 to b do
            begin
                s2 := s2 + '0';
            end
        end
    end
    else
    begin
        if b > 0 then
        begin
            s2 := '.';
            for i := 1 to b do
            begin
                s2 := s2 + IntToChr(IntPart(n * 10));
                n := FracPart(n * 10)
            end
        end
    end;
   writeln(s2)
end.

При b=3 и n равном 5.5 или 5.4 программа выдает корректные ответы .500 и .400. Однако при b=3 и n равном 5.6 или 5.3 программа выдает ошибки вида .599 и .299.

Насколько я понял, ошибка происходит в подпрограмме IntPart, ответственной за выделение целой части дробного числа. В определенной ситуации я ожидаю, что в условии if значение n - round(n) будет 0, а на практике, из-за погрешности вычислений типа real, значением будет -0,00000000001. Из-за этого оператор ветвления отработает не так, как я изначально этого ожидал.

Как устранить влияние погрешности при вычислениях с использованием типа Real в этой и в других моих будущих программах?

 ,

KOMMUNIST90
()

Неправильная компиляция в Pascal при использовании переменных типа string

Не могу заставить корректно работать программы с использованием нескольких переменных типа string на pascal.

Простой пример:

program Example;
var
    s1, s2: string;
begin
    writeln('Enter the first string');
    read(s1);
    writeln('Enter the second string');
    read(s2);
    writeln(s1);
    writeln(s2)
end.

Запущенная скомпилированная программа почему-то отказывается читать вторую переменную и выдает:

Enter the first string
12
Enter the second string
12

При этом та же самая программа, скомпилированная на телефоне (приложение Pascal N-IDE), работает корректно.

В чём может быть причина ошибки?

 ,

KOMMUNIST90
()

Очистка потока ввода с помощью readln в Pascal

Добрый день!

В процессе изучения учебника Столярова «Азы программирования» в главе 2.5.2 «Посимвольный ввод информации» возник вопрос.

В тексте книги описывается программа для перемножения двух заданных чисел. Предварительно правильность написания чисел проверяется с помощью процедуры ReadLongint

program MultReadLongint;

procedure ReadLongint(var ok: boolean; var EndResult: longint);
var
    c: char;
    posit: integer;
    res: longint;
begin
    res := 0;
    posit := 0;
    repeat
        read(c);
        posit := posit + 1
    until (c <> ' ') and (c <> #10);
    while (c <> ' ') and (c <> #10) do
        begin
            if (c < '0') or (c > '9') then
            begin
                writeln('Incorrect simbol ', c, ' on the position ', posit);
                readln;
                ok := false;
                exit
            end;
            res := res * 10  + ord(c) - ord('0');
            read(c);
            posit := posit + 1
        end;
    EndResult := res;
    ok := true
end;

var
    a, b: longint;
    ok: boolean;
begin
    writeln('Enter the first number: ');
    repeat
        ReadLongint(ok, a);
    until ok;
    writeln('Enter the second number: ');
    repeat
        ReadLongint(ok, b);
    until ok;
    writeln(a, ' times ', b, ' is ', a*b)
end.

Но у такой программы есть слабое место. При вводе первого числа как ‘3 5’ программа будет считать, что последовательно введены число №1 - 3 и число №2 - 5.

Причина понятна - у автора пробел прерывает цикл, но поток ввода не очищается => число 5 уже находится в потоке ввода при новом старте процедуры для числа №2.

Собственно, один вариант решения проблемы я нашёл: переписать в процедуре ReadLongint участок с

    while (c <> ' ') and (c <> #10) do

на

    while c <> #10 do

Но потом я попробовал решить проблему еще одним способом - в первоначальный вариант программы добавить в конце процедуры ReadLongint очистку потока ввода c помощью readln. т.е. с

        end;
    EndResult := res;
    ok := true
end;

поменять на

        end;
    readln;
    EndResult := res;
    ok := true
end;

Но результат получился не таким, как я ожидал. Все работает корректно если первое число равно, к примеру, ’3 ’. Если же число равно просто ‘3’, то после выполнения readln сработает классическим для себя образом - переведет строку и будет ждать ввода Enter.

Почему же тогда в месте, где его изначально использовал автор,

 if (c < '0') or (c > '9') then
            begin
                writeln('Incorrect simbol ', c, ' on the position ', posit);
                readln;
                ok := false;
                exit
            end;

readln работает всегда безошибочно не переводя строку?

 ,

KOMMUNIST90
()

Лишний writeln в Pascal?

Добрый день!

Занимаюсь решением задач из задачника Андрея Столярова. В процессе решения задачи 2.15. возник вопрос.

Задача следующая:

"Написать программу, запрашивающую два числа (высоту буквы и количество таких букв) и печатающую нужное количество букв Z заданной высоты, причем каждая следующая буква Z должна отстоять от предыдущей на один пробел по горизонтали и на половину высоты буквы по вертикали. Например, для чисел 7 и 3 картина должна получиться такая:

*******
     *
    *
******* *******
  *          *
 *          *
******* ******* *******
          *          *
         *          *
        ******* *******
                  *
                 *
                *******

Программу написал следующую:

rogram z_sequence;

procedure PrintChars(count: integer);
var
    i: integer;
begin
    for i := 1 to count do
        write('*')
end;

procedure PrintSpaces(count: integer);
var
    i: integer;
begin
    for i := 1 to count do
        write(' ')
end;

procedure PrintLineOfDiamondUp(i, z: integer);
begin
    if i = 1 then
        PrintChars(z)
    else
        begin
            PrintSpaces(z - i);
            PrintChars(1)
        end;
    writeln
end;

procedure PrintLineOfDiamondDown(i, z: integer);
begin
    if i = 1 then
        exit
    else
        begin
            PrintSpaces(i - 1);
            PrintChars(1)
        end
end;

procedure PrintFirstHalf(z: integer);
var
    i: integer;
begin
    for i := 1 to (z div 2) do
        PrintLineOfDiamondUp(i, z)
end;

procedure PrintFirstCenterBar(z, n: integer);
begin
    if n > 1 then
        begin
            PrintChars(z);
            PrintSpaces(1);
            PrintChars(z);
            writeln
        end
    else
        PrintChars(z);
        writeln
end;

procedure PrintHalf(i, a, z: integer);
var
    count: integer;
begin
    if i = 1 then
    begin
        for count := (z div 2) downto 2 do
            begin
                PrintSpaces(a * z + a);
                PrintLineOfDiamondDown(count, z);
                writeln
            end
    end;
    if i > 1 then
    begin
        for count := (z div 2) downto 2 do
            begin
                PrintSpaces(a * z + a);
                PrintLineOfDiamondDown(count, z);
                PrintSpaces((z div 2 + 1) + 1 + (z - 2));
                PrintChars(1);
                writeln
            end
    end
end;

procedure PrintBar(i, a, z: integer);
begin
    if i = 1 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        writeln
    end;
    if i = 2 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end;
    if i >= 3 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end
end;

var
    i, n, a, z: integer;
begin
    repeat
        writeln('Enter the letter''s height (5 or more, odd)');
        readln(z);
    until (z >= 5) and (z mod 2 = 1);
    repeat
        writeln('Enter the number of the letters');
        readln(n);
    until (n >= 1);
    PrintFirstHalf(z);
    PrintFirstCenterBar(z, n);
    a := 0;
    for i := n downto 1 do
        begin
            PrintHalf(i, a, z);
            PrintBar(i, a, z);
            a := a + 1
        end
end.

Однако, она выдавала такой результат:

*******
     *
    *
******* *******

  *          *
 *          *
******* ******* *******
          *          *
         *          *
        ******* *******
                  *
                 *
                *******

т.е. после первой центральной черты печатается лишний перенос строки.

Опытным путем понял, что если в процедуре PrintFirstCenterBar убрать writeln и напечатать вместо

procedure PrintFirstCenterBar(z, n: integer);
begin
    if n > 1 then
        begin
            PrintChars(z);
            PrintSpaces(1);
            PrintChars(z);
            writeln
        end
    else
        PrintChars(z);
        writeln
end;
procedure PrintFirstCenterBar(z, n: integer);
begin
    if n > 1 then
        begin
            PrintChars(z);
            PrintSpaces(1);
            PrintChars(z);
        end
    else
        PrintChars(z);
        writeln
end;

программа станет работать корректно.

Но почему так происходит?

Ни PrintChars ни PrintSpaces не печатает перенос строки. В условии после else стоит writeln и если попросить программу напечатать одну букву Z лишнего переноса не произойдет.

Подпрограмма PrintBar

procedure PrintBar(i, a, z: integer);
begin
    if i = 1 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        writeln
    end;
    if i = 2 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end;
    if i >= 3 then
    begin
        PrintSpaces(a * z + a);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        PrintSpaces(1);
        PrintChars(z);
        writeln
    end
end;

очень похожа по своей структуре на PrintFirstCenterBar, но там все writeln стоят на своём месте и никакого лишнего переноса строки не вызывают.

Так почему же тогда в подпрограмме PrintFirstCenterBar оператор writeln является лишним?

 

KOMMUNIST90
()

Помогите разобраться с рекурсией

Добрый день!

Изучаю программирование по учебнику Андрея Столярова. Тема - язык Паскаль и начала программирования. Вопрос касается, в частности, решения задачи 2.12. из задачника того же автора.

"Необходимо построить «ромбик»/«алмаз» из пробелов на фоне, заполненном звездочками. Так, если ввести число 5, программа должна напечатать следующее: "

*******
*** ***
**   **
*     *
**   **
*** ***
*******

Задачу легко решил следующим образом, всё прекрасно работает:

program diamond_cutout;

procedure PrintChars(count: integer);
var
    i: integer;
begin
    for i := 1 to count do
        write('*')
end;

procedure PrintSpaces(count: integer);
var
    i: integer;
begin
    for i := 1 to count do
        write(' ')
end;

procedure PrintLineOfDiamond(i, HH: integer);
begin
    PrintChars(HH + 2 - i);
    PrintSpaces(2 * i - 1);
    PrintChars(HH + 2 - i);
    writeln
end;

procedure PrintFirstLastLineOfDiamond(H: integer);
begin
    PrintChars(H + 2);
    writeln
end;

var
    HalfHeight, i, Height: integer;
begin
    repeat
        write('Enter the diamond''s height (positive, odd)');
        readln(Height);
    until (Height > 0) and (Height mod 2 = 1);
    HalfHeight := Height div 2;
    PrintFirstLastLineOfDiamond(Height);
    for i := 1 to (HalfHeight + 1) do
        PrintLineOfDiamond(i, HalfHeight);
    for i := HalfHeight downto 1 do
        PrintLineOfDiamond(i, HalfHeight);
    PrintFirstLastLineOfDiamond(Height)
end.

Затем, поскольку недавно я прошёл материал по рекурсии, я решил заменить процедуры PrintChars и PrintSpaces на аналогичные, но с использованием рекурсии вместо цикла.

т.е. с

procedure PrintChars(count: integer);
var
    i: integer;
begin
    for i := 1 to count do
        write('*')
end;

на

procedure PrintChars(count: integer);
begin
    if count > 0 then
    begin
        write('*');
        PrintChars(count - 1)
    end
end;

Однако, хотя на выходе (по моему мнению) я должен был получить точно такой же результат как и раньше, я получил

*******
*** ***
** ****
* *****
** ****
*** ***
*******

Это означает, что я что-то неправильно понял в материале по рекурсии. В чём моя ошибка? Почему модифицированный код даёт другой результат?

 ,

KOMMUNIST90
()

Как сопировать текст из vim в буфер обмена

Как скопировать в Vim текст и вставить во внешнюю программу? Предлагаемое в сети решение "*y у меня не работает.

 

KOMMUNIST90
()

Проблема с настройкой яркости монитора из командной строки Linux

Решил научится через терминал научиться регулировать яркость монитора - чтобы ночью не бил по глазам. Исполнил рекомендации данной статьи (http://rus-linux.net/MyLDP/consol/adjust_monitor_brightness_from_command_line.html) и все прошло как по маслу. Однако через несколько дней на ту же самую команду

xrandr --output eDP-1 --brightness 0.35

стал поступать ответ

warning: output eDP-1 not found; ignoring
xrandr: Need crtc to set gamma on.

Пребываю в недоумении, как все могло поломаться всего за несколько дней. Единственное что возможно могло повлиять на ситуацию, это моя недавняя борьба с тирингом (Артефакты изображения в IceWM)

Система:

  • DELL LATTITUDE E7250;
  • Ubuntu 20.04;
  • Оконный менеджер IceWM;

 , , ,

KOMMUNIST90
()

Артефакты изображения в IceWM

Установил оконный менеджер IceWM.

В браузере Google Chrome при скролле страницы по изображению пробегает косая черта по диагонали примерно на полэкрана. Мне даже первое время казалось что у меня треснул экран. Однако когда не скроллишь - картинка полностью нормальная. Также артефакт не удалось заскринить - все скриншоты полностью нормальные. В установленных здесь же на этой же машине Ubuntu и Windows 10 ничего подобного не наблюдается.

При воспроизведении видео в VLC бывает появляется такой же артефакт, также иногда картинка «зашумляется». Однако при воспроизведении видео с YouTube из того же Google Chrome картинка полностью нормальная.

Система:

  • DELL LATTITUDE E7250;
  • Ubuntu 20.04;
  • Оконный менеджер IceWM;
  • Модель видеокарты - Intel Corporation HD Graphics 5500 (rev 09)
  • Видеодрайвер - i915

 ,

KOMMUNIST90
()

Bluetooth наушники - сопряжение есть - звука нет

Решил через терминал подключить свои bluetooth наушники. Вооружился данной статьей (https://losst.ru/nastrojka-bluetooth-ubuntu-16-04). После ввода заветного

sudo rfcomm connect hci0 D0:B3:3F:96:2A:30 2

терминал отрапортовал «connected», а наушники подали привычный сигнал о сопряжении. Однако при попытке что-то воспроизвести продолжают звучать основные динамики ноута, а наушники молчат.

P.S. Дело было в оконном менеджере IceWM. В родном окружении Ubuntu при сопряжении через графический интерфейс все работает как надо.

Система:

  • DELL LATTITUDE E7250;
  • Ubuntu 20.04;
  • Оконный менеджер IceWM;
  • Наушники DEXP BT-25.

 

KOMMUNIST90
()

RSS подписка на новые темы