Страницы

Поиск по вопросам

Показаны сообщения с ярлыком функции. Показать все сообщения
Показаны сообщения с ярлыком функции. Показать все сообщения

понедельник, 13 апреля 2020 г.

Функция fread: остановится ли считывание при наличиии \0?

#c #файлы #функции

                    
Здравстdуйте! Допустим у нас есть файл. Мы его открыли, а потом считываем оттуда
информацию с помощью fread. Вопрос: а если в файле есть \0, то fread остановится считывать
информацию на \0 или будет дальше считывать информацию до конца файла.    


Ответы

Ответ 1



Вопрос на самом деле не дурацкий :-) Вообще - чтение файла ф-цией fread не заканчивается при достижении какого-либо символа, только лишь при ошибках ввода-вывода или если файл закончился. Посмотреть описание ф-ции можно в справочниках. Например, здесь или здесь

вторник, 31 марта 2020 г.

Как в C передать в функцию переменное количество аргументов?

#c #функции


Есть функция, которая завершает программу, выполняя занесение отладочной информации
в файл лога:

void crash(const char *const _fstr, ...)
{
    if ( (_fstr != NULL) && (strlen(_fstr) > 0) )
    {
        FILE *f = fopen(CRASH_LOG_FILE_NAME, "a");
        fprintf(f, _fstr, ???);
        fclose(f);
    }

    abort();
}


Я хочу сделать, чтобы в функцию crash() передавалась строка формата и какое-то количество
аргументов. Ситуации бывают разные. При некоторых крашах в файл желательно заносить
не только строку с описанием проблемы, но еще и ряд кодов, например GetLastError()
и WSAGetLastError().

Как это сделать?
    


Ответы

Ответ 1



Вам нужно использовать функцию vfprintf, а передавать ей аргумент типа va_list, который получаете в своей функции примерно как void crash(const char *const _fstr, ...) { va_list ap; va_start(ap, _fstr); ... vfprintf(f,_fstr,ap); ... va_end(ap);

понедельник, 30 марта 2020 г.

Сократить запись функции для оптимизации

#php #функции #оптимизация


есть функция, в которой на вход подается два значения.
как сократить в ней количество операторов сравнения и условий, для оптимизации, или
это уже итак минимум?

function test($x){
    if ($x!==0 && $x!==1) {
        return;
    }
    elseif($x == 0) {
        echo 1;
    }
    else
        echo 0;
}

    


Ответы

Ответ 1



function test($x){ if ($x == 1) { echo 0; } elseif ($x == 0) { echo 1; } }

Ответ 2



как сократить количество операторов сравнения и условий, для оптимизации, или это уже итак минимум? Можно существенно сократить код, используя вместо условий массив в комбинации с коалесцентным оператором: function test($x, $a = [1, 0]) { echo $a[$x] ?? null; } Вызов test(0) выведет 1, test(1) - 0. Все остальные варианты - null.

Ответ 3



нашел три варианта решения 1 - echo !$x; 2 - echo 1-$x; 3 - echo ($x ? 0 : 1); если вдруг кому будет необходимо :)

пятница, 20 марта 2020 г.

Вычисление чётного факториала

#javascript #html #алгоритм #функции


Не понимаю, почему выскакивает ошибка и как с ней справиться. Пример вычисления факториала
6!! = 6*4*2*1 = 48



    function doubleFactorial(n) {
        return (n !== 1) ? n * doubleFactorial(n - 2) : 1;
    }
    console.log(doubleFactorial(6));
    console.log(doubleFactorial(7));



    


Ответы

Ответ 1



function doubleFactorial(n) { return (n > 1) ? n * doubleFactorial(n - 2) : 1; } console.log(doubleFactorial(6)); console.log(doubleFactorial(7));

пятница, 13 марта 2020 г.

Странная работа оконных функций min max в ms sql 2012 при обработке null

#sql_server #функции


Странная работа оконных функций min, max в ms sql 2012 при обработке null–значений.
2 запроса

select max(q1) OVER(PARTITION BY q2 order by q1) from
(
    select null as q1, 1 as q2, 0 as q3
    union
    select 11, 1, 1
    union
    select null, 1, 1
) a


select max(q1) OVER(PARTITION BY q2) from
(
    select null as q1, 1 as q2, 0 as q3
    union
    select 11, 1, 1
    union
    select null, 1, 1
) a


Результат первого


NULL 
NULL
11


Второго 


11
11
11


Казалось бы причём тут в функциях min max order by...


  Microsoft SQL Server 2012 - 11.0.5058.0 (X64)
    May 14 2014 18:34:29 
    Copyright (c) Microsoft Corporation
    Enterprise Edition: Core-based
  Licensing (64-bit) on Windows NT 6.1  (Build 7601: Service Pack
  1)


и


  Microsoft SQL Server 2014 - 12.0.2000.8 (X64)
    Feb 20 2014 20:04:26
    Copyright (c) Microsoft Corporation
    Enterprise Edition (64-bit) on
  Windows NT 6.3  (Build 9600: )

    


Ответы

Ответ 1



Немного модифицируем ваш запрос: select max(q1) OVER(PARTITION BY q2 order by q3), min(q1) OVER(PARTITION BY q2 order by q3), sum(q3) OVER(PARTITION BY q2 order by q3), max(q1) OVER(PARTITION BY q2), sum(q3) OVER(PARTITION BY q2) from ( select null as q1, 1 as q2, 0 as q3 union select 12, 1, 1 union select 11, 1, 2 ) a Результат: NULL NULL 0 12 3 12 12 1 12 3 12 11 3 12 3 Видно, что у выражений без ORDER BY берется требуемое значение для всего окна, а для предложений с сортировкой - берется накопленное на данный момент значение в порядке сортировки. Теперь открываем документацию, раздел "Общие примечания": Если предложение ORDER BY не указано, то для рамки окна используется весь раздел. Это относится только к тем функциям, которым не требуется предложение ORDER BY. Если предложение ROWS или RANGE не указаны, а указано предложение ORDER BY, то в качестве значения по умолчанию для рамки окна используется RANGE UNBOUNDED PRECEDING AND CURRENT ROW Собственно в документации сказано именно то, что мы увидели в результате запроса. Если order by указан, то функции к которым это применимо, рассматривают окно он начала раздела заданного partition by до текущей строки.

воскресенье, 8 марта 2020 г.

Определение вызывающего родителя

#php #функции


Функция_А вызывает функцию_Б.

Подскажите пожалуйста, можно ли как-то находясь внутри функции_Б определить какая
функция её вызвала?

И ещё интересует другая ситуация, а если вместо функции_А например просто скрипт,
можно ли как-то определить это, чтобы например показало "main".
    


Ответы

Ответ 1



Обычно, для этого получают стек вызова при помощи функции debug_backtrace(). Первый элемент будет содержать информацию о текущей функции, второй - об непосредственно родителе. Например: "; $funcs = debug_backtrace(); echo "Parent: ". $funcs[1]['function']; } function a($str) { b($str); } a('Hello world!'); Результат From b: Hello world! Parent: a

суббота, 7 марта 2020 г.

Разбиение кода на функции. С++

#cpp #функции


Есть код программы, которая позволяет ввести динамический массив, а затем
1. Упорядочивает элементы массива по возрастанию.
2. Находит сумму отрицательных элементов массива.
3. Находит произведение элементов массива, расположенных между максимальным и минимальным
элементами.
Не получается грамотно разбить код на многофайловый проект. 

#include "stdafx.h"  
#include   
using namespace std;    
double *p, n;
double pr = 1;
int sum, min = 1000000, max = -1000000, i_, j_;

int main()
{
    setlocale(LC_ALL, "Russian");
    cout << "Введите количество элементов: ";
    cin >> n;
    p = new double[n];

    cout << "Введите элементы массива: ";
    for (int i = 0; i < n; i++)
        cin >> p[i];

    for (int i = 0; i < n; i++) {
        if (p[i] < 0) sum += p[i];
        if (max < p[i]) { max = p[i]; i_ = i; }
        if (min > p[i]) { min = p[i]; j_ = i; }
    }

    if (i_ > j_) swap(i_, j_);
    for (int i = (i_ + 1); i < j_; i++)
        pr *= p[i];

    for (int i = 0; i < n; i++)
        for (int j = i; j > 0 && p[j - 1] > p[j]; j--)
            swap(p[j], p[j - 1]);

    cout << "Отсртированный массив: ";
    for (int i = 0; i < n; i++)

        cout << p[i] << " ";
    cout << endl;
    cout << "Сумма отрицатльных элементов: " << sum <


Ответы

Ответ 1



Ты уже большую часть ответа написал в вопросе. Нужно выделить такие функции и соответствующим образом их вызвать: Ввести динамический массив Упорядочивает элементы массива по возрастанию. Находит сумму отрицательных элементов массива. Находит позицию минимального элемента Находит положение максимального элемента Находит произведение элементов массива между заданными позициями Выводит массив

Ответ 2



Ну, я бы делал примерно так, с обязательным уничтожением глобальных переменных как класса: #include using namespace std; void inputArray(double*&p, int&size) { cout << "Введите количество элементов: "; cin >> size; p = new double[size]; cout << "Введите элементы массива:\n"; for (int i = 0; i < size; i++) { cout << "p[" << i << "] = "; cin >> p[i]; } } void outArray(double*p, int size, const char * msg) { cout << msg << "\n"; for(int i = 0; i < size; ++i) cout << p[i] << " "; cout << "\n"; } inline void swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } void sortArray(double*p, int size) { for (int i = 0; i < size; i++) for (int j = i; j > 0 && p[j - 1] > p[j]; j--) swap(p[j], p[j - 1]); } double negSum(double*p, int size) { double sum = 0.0; for (int i = 0; i < size; i++) if (p[i] < 0) sum += p[i]; return sum; } void minmax(double*p, int size, int& min_idx, int& max_idx) { double min = p[min_idx = 0], max = p[max_idx = 0]; for(int i = 1; i < size; ++i) { if (p[i] < min) { min = p[min_idx = i]; } else if (p[i] > max) { max = p[max_idx = i]; } } } double product(double*p, int min_idx, int max_idx) { double prod = 1.0; for(int i = min_idx; i <= max_idx; ++i) prod *= p[i]; return prod; } int main() { setlocale(LC_ALL, "Russian"); double * p; int size; inputArray(p,size); outArray(p,size,"Исходный массив"); cout << "Сумма отрицательных элементов = " << negSum(p,size) << endl; int min_idx, max_idx; minmax(p,size,min_idx,max_idx); if (max_idx == min_idx) { cout << "Между максимальным и минимальным значениями нет чисел" << endl; } else { cout << "Произведение между максимальным и минимальным элементами: " << product(p,min_idx,max_idx) << endl; } sortArray(p,size); outArray(p,size,"Отсортированный массив"); delete[] p; }

Объявление и определение функции (порядок размещения)

#cpp #функции


В каждой единице трансляции, объявление функции обязано предшествовать её вызову.
Если это условие выполняется, то порядок размещения определения уже не имеет особого
значения. 

Какой вариант предпочтительней и почему?
(сначала функция объявлена, а затем определена или наоборот)

void someFunction(); // declaration
void someFunction() { cout << "hello" << endl; }

int main()
{
    someFunction(); // function call                
    return 0;
}


или

void someFunction() { cout << "hello" << endl; }
void someFunction(); // declaration

int main()
{
    someFunction(); // function call                
    return 0;
}

    


Ответы

Ответ 1



Какой вариант предпочтительней и почему? (сначала функция объявлена, а затем определена или наоборот) Хотелось бы узнать причину именно такого порядка размещения. Что это? удобство восприятия программы, отдельное положение стандарта языка, код в этом случае быстрее компилируется или есть ещё какая-то причина? Причина та же самая, по которой вообще в язык добавлена возможность объявлять функции без их определения. Представьте что возможности объявить (без определения!) функции нет. И вам надо написать две функции, вызывающие друг друга: void SomeFirstFunction(int n) { cout << n << endl; SomeSecondFunction(n); } void SomeSecondFunction(int n) { if (n > 0) { SomeFirstFunction(n - 1); } } int main() { SomeFirstFunction(5); } Компилятор разбирает файл сверху вниз. Он доходит до строки SomeSecondFunction(n); В этот момент он понятия не имеет, что это за SomeSecondFunction и какие параметры она принимает - она ведь еще не определена. И он падает с ошибкой: In function 'void SomeFirstFunction(int)': error: 'SomeSecondFunction' was not declared in this scope Перестановка местами SomeSecondFunction и SomeFirstFunction, очевидно, не поможет. Поэтому в язык добавлен хак - вы можете сказать компилятору "SomeSecondFunction - это функция вот с такими параметрами, и она будет определена где-то ниже. Или даже не ниже, а вообще в другом файле - вобщем, смело считай это функцией, генерируй код для ее вызова, потом линковщик разберется где эта функция на самом деле лежит". // предупредили компилятор void SomeSecondFunction(int n); void SomeFirstFunction(int n) { cout << n << endl; // компилятор спокойно сгенерировал вызов еще не определенной (но объявленной!) функции SomeSecondFunction(n); } void SomeSecondFunction(int n) { if (n > 0) { SomeFirstFunction(n - 1); } } При этом нет никакого смысла в объявлении SomeFirstFunction после ее определения - компилятор и так знает что это за функция, ему от еще одного объявления уже известного факта лучше не станет. А вот в примерах из вашего вопроса объявление полностью избыточно. Т.е. ни один из этих вариантов не предпочтительней - они оба бессмысленны, т.к. эквивалентны варианту вообще без declaration: void someFunction() { cout << "hello" << endl; } int main() { someFunction(); // function call return 0; }

Ответ 2



Делать "наборот" нет никакого смысла. Потому что определение функции уже включает в себя объявление. Причина, по которой нужно объявлять до ее использования очень проста. Когда компилятор встречает вызов функции, ему нужно коректно передать параметры, а также правильно сформировать их "проталкивание" в стек/регистры. А также и решить, кто будет "подчищать" стек после вызова. Старый си допускал использование функций до их определений/объявлений, но при этом он считал, что все аргументы функции типа int и возвращаемое значение такое же. UPD кратко. Это запрещено стандартом явно 3.2 One definition rule [basic.def.odr] 1 No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, or template. расширено, но сумбурно В теории, все функции можно разместить так, что бы вызываемые функции были выше (раньше) вызываемых. Это конечно требует дополнительных телодвижений (читать: компиляция будет дольше или нужно ручками все будет расставлять), но возможно. Либо добавлять определение к каждой единице, но это также сильно удлинит компиляцию. Плюс возникает риск того, что в разных единицах компиляции будут разные версии одной и той же функции (из за дефайнов, а это уже весело) либо просто будет дублирование кода, который линковщику ещё и убрать нужно. Поэтому это и запрещено стандартом ( Просто выделение объявлений позволяет делать компиляцию в паралель (или даже на разных машинах). Минимизируется наличие дубликатов функций (inline функции могут дублироваться).

пятница, 28 февраля 2020 г.

Можно ли напрямую в функцию передать массив?

#cpp #массивы #функции #случайные_числа


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

int randint (int min, int max, int exceptions{}) { ... }


Как мне написать параметры функции, чтобы я мог вызывать функцию таким образом:

randint (0, 10, {4, 5, 6});


Мне нужна именно передача массива напрямую.
    


Ответы

Ответ 1



Возможны несколько вариантов: int randint (int min, int max, ::std::initializer_list values) int randint (int min, int max, ::std::array values) int randint (int min, int max, ::std::array_view values) // аналог gsl::span Использовать указатели для передачи массива в функцию не следует.

функция для сортировка многомерного массива

#php #массивы #функции #сортировка


есть массив

$menu = [
     'tasks' => [
         'title' => 'Задачи',
         'path' => '/route/tasks/',
         'sort' => 3
     ],
     'obj' => [
         'title' => 'Цели',
         'path' => '/route/obj/',
         'sort' => 2
     ],
     'index' => [
         'title' => 'Главная',
         'path' => '/route/index/',
         'sort' => 1
     ],
     'message' => [
         'title' => 'Сообщения и предложения',
         'path' => '/route/message/',
         'sort' => 5
     ],
     'friends' => [
         'title' => 'Друзья',
         'path' => '/route/friends/',
         'sort' => 4
         ]
];


нужно отсортировать его по ключу 'sort'.
через функцию usort сортируется как надо:

$key = 'sort';
$sort = 'desc';

usort($menu, function ($a, $b) use ($key, $sort) {
    if ($sort == 'asc') {
        if ($a[$key] == $b[$key]) {
          return 0;
        }
        return ($a[$key] > $b[$key]) ? 1 : -1;
    }
    if ($sort == 'desc') {
        if ($a[$key] == $b[$key]) {
          return 0;
        }
        return ($a[$key] > $b[$key]) ? -1 : 1;
    }
});

echo '
';
var_dump($menu);


вывод: 

array(5) {
  [0]=>
  array(3) {
    ["title"]=>
    string(44) "Сообщения и предложения"
    ["path"]=>
    string(15) "/route/message/"
    ["sort"]=>
    int(5)
  }
  [1]=>
  array(3) {
    ["title"]=>
    string(12) "Друзья"
    ["path"]=>
    string(15) "/route/friends/"
    ["sort"]=>
    int(4)
  }
  [2]=>
  array(3) {
    ["title"]=>
    string(12) "Задачи"
    ["path"]=>
    string(13) "/route/tasks/"
    ["sort"]=>
    int(3)
  }
  [3]=>
  array(3) {
    ["title"]=>
    string(8) "Цели"
    ["path"]=>
    string(11) "/route/obj/"
    ["sort"]=>
    int(2)
  }
  [4]=>
  array(3) {
    ["title"]=>
    string(14) "Главная"
    ["path"]=>
    string(13) "/route/index/"
    ["sort"]=>
    int(1)
  }
}


делаю функцию, чтобы принимать значение $array, $key, $sort:

function array_sort($array, $key, $sort) {
   return usort($array, function($a, $b) use ($key, $sort) {
        if ($sort == 'asc') {
            if ($a[$key] == $b[$key]) {
                return 0;
            }
            return ($a[$key] > $b[$key]) ? 1 : -1;
        }
        if ($sort == "desc") {
            if ($a[$key] == $b[$key]) {
            return 0;
        }
        return ($a[$key] > $b[$key]) ? -1 : 1;
        }
    });
}
array_sort($menu, $key = 'sort', $sort = 'asc');
echo '
';
var_dump($menu);


вывод:

array(5) {
  ["tasks"]=>
  array(3) {
    ["title"]=>
    string(12) "Задачи"
    ["path"]=>
    string(13) "/route/tasks/"
    ["sort"]=>
    int(3)
  }
  ["obj"]=>
  array(3) {
    ["title"]=>
    string(8) "Цели"
    ["path"]=>
    string(11) "/route/obj/"
    ["sort"]=>
    int(2)
  }
  ["index"]=>
  array(3) {
    ["title"]=>
    string(14) "Главная"
    ["path"]=>
    string(13) "/route/index/"
    ["sort"]=>
    int(1)
  }
  ["message"]=>
  array(3) {
    ["title"]=>
    string(44) "Сообщения и предложения"
    ["path"]=>
    string(15) "/route/message/"
    ["sort"]=>
    int(5)
  }
  ["friends"]=>
  array(3) {
    ["title"]=>
    string(12) "Друзья"
    ["path"]=>
    string(15) "/route/friends/"
    ["sort"]=>
    int(4)
  }
}


подскажите пожалуйста, почему не работает функция, в чем я допустил ошибку? 
    


Ответы

Ответ 1



Вы передаете в свою функцию массив по значению, а не по ссылке. В итоге функция array_sort принимает копию исходного массива и сортирует ее не трогая оригинальный массив. Измените объявление функции на function array_sort(&$array, $key, $sort) { ^^^ и все будет работать

среда, 26 февраля 2020 г.

Нужно ли дублировать static в определении функции?

#c #функции #static


Правильно ли я понимаю, что если функция объявлена со спецификатором static, то его
не надо дублировать в определении?

static void f(void);

void f(void)
{
    // ...
}

    


Ответы

Ответ 1



Нет, не надо. Более того, даже если вы ее ниже явно объявите как extern, она все равно останется static static void f(void); extern void f(void) // Все равно функция имеет внутреннее связывание { // ... } В стандарте языка (6.2.2 Linkages of identifiers) собственно поведение объявления без явного спецификатора класса хранения (ваше void foo(void)) по определению эквивалентно ему же с спецификатором extern (как у меня) (6.2.2/5). А поведение объявления с extern в свою очередь наследуется из того, что было объявлено выше (6.2.2/4).

PHP: Как называется данная цепочка функций?

#php #классы #функции


Подскажите пожалуйста как называется данная цепочка функций и как её организовать? 

/*class Example*/
(new Example)->functionOne()->functionTwo();


и возможна ли такая связка?

/*class Example*/
(new Example)->functionOne($one)->functionTwo($two);


Лучше будет если направить на литературу. Я либо плохо гуглил, либо что-то не заметил.
    


Ответы

Ответ 1



Такой приём называется чейнингом. Можете почитать о нём, например на хабре. Его суть заключается в том, что методы возвращают ссылку на экземпляр объекта. Это может быть как объект, в котором этот метод вызывался (как у вас в примере), так и любой другой. /** * Все методы класса Example возвращают ссылку на себя */ class Example { function exampleFunctionOne() { echo "exampleFunctionOne\n"; return $this; } function exampleFunctionTwo() { echo "exampleFunctionTwo\n"; return $this; } } /** * Метод класса Example2 принимает в качестве аргумента экземпляр * класса Example и возвращают ссылку на него */ class Example2 { function example2FunctionOne($example) { echo "example2FunctionOne\n"; return $example; } } $e = new Example(); $e->exampleFunctionOne()->exampleFunctionTwo(); $e2 = new Example2(); $e2->example2FunctionOne($e)->exampleFunctionTwo();

как определить inline функцию?

#cpp #функции #inline


Как определить встроилась ли функция или у нее свой адрес и реализация, как у не
встраимовой функции? Компилятор может игнорировать инструкцию, тем более если я запрашиваю
адрес функции. А как определить непонятно, только в ассемблер-код смотреть?

#include 

void func () {
    std::cout << "func";
}

inline void func2() { func(); }


int main()
{
    std::cout << uint64_t(&func) << " || " << uint64_t(&func2) << std::endl;
    // Пример вывода: 4199872 || 4204384
    return 0;
}

    


Ответы

Ответ 1



Вот так и определять. С помощью inline, понимая, что компилятор может ее встроить, может не встроить, а может встроить и без всякого inline... В некоторых компиляторах есть расширения, которые заставляют компилятор прибегнуть ко встраиванию (например, __forceinline в Visual C++), но это уже нестандарт...

вторник, 25 февраля 2020 г.

Как работает typedef в этом коде?

#функции #typedef #cpp


#include 
using namespace std;

typedef int (*func)(int a, int b);

int call_func(int a, int b)
{
    return a + b;
}

void function(int a, int b, func f);

int main ()
{

    function(5, 2, &call_func);
    system("pause");
}

void function(int a, int b, func f)
{
    int sum = (*f)(a, b);
    std::cout << "Sum = " << sum << std::endl;
}

typedef вводит новое имя для типа, и примеры вида typedef int int32 думаю понятны
любому. Ну а что с typedef int (*func)(int a, int b); в этом коде, он же должен вместо
func f подставить int (int a, int b) f, а подставляет int f(int a, int b), хотя я не
только это не понимаю, а и то, откуда он знает что заменять нужно func, ведь это неполный
тип или имя. Описание typedef в интернете заканчиваются на typedef int int32 или же
просто с примером структур и функций или ещё каких сложных типов, но без объяснений.
Я уже не знаю что думать, с дефайном было как-то проще.    


Ответы

Ответ 1



typedef -- не просто текстовая подстановка. Он работает следующим образом: если X -- имя typedef-типа, объявление переменной X x; работает как будто бы на этом месте стоит описание типа в typedef, где X заменено на x. То есть typedef int (*func)(int a, int b); func f; работает как int (* /*было func, стало*/ f )(int a, int b);

Передача двумерного массива в функцию (Rust)

#массивы #функции #rust


Кто-нибудь может подсказать, как передать в функцию двумерный массив?

Желательно по ссылке, т.е. без копирования самого массива. К сожалению их "книга"
пока не богата на примеры передачи параметров в функцию. Вот как я себе это пытался
представить:

fn foo(a:&[&[f64]],x:&[f64])
{
    for i in 0..3 {
        for j in 0..4 {
            println!("{}",a[i][j]);
        }
    }
}

fn main()
{
    let A:[[f64;4];3]=[[1.1,-0.2, 0.1,1.6],
                       [0.1,-1.2,-0.2,2.3],
                       [0.2,-0.1, 1.1,1.5]];
    let mut X:[f64;3]=[0.0;3];

    foo(&A,&X);
}


"X" передается без проблем, а вот на "А" ругается при всех комбинациях [ ] и &, которые
я смог придумать:

src/main.rs:16:6: 16:8 error: mismatched types:
 expected `&[&[f64]]`,
    found `&[[f64; 4]; 3]`
(expected slice,
    found array of 3 elements) [E0308]
src/main.rs:16  foo(&A,&X);
                    ^~
error: aborting due to previous error
Could not compile `example`.

    


Ответы

Ответ 1



Дело в том, что, к примеру, выражение [1i32, 2, 3] возвращает тип [i32; 3] — массив с заданной при компляции длиной. Когда же &[f64] является срезом — ссылочным типом, состоящим из указателя на массив и длины (известной во время выполнения). Вот так будет работать: fn foo(a:&[&[f64]],x:&[f64]) { for i in 0..3 { for j in 0..4 { println!("{}",a[i][j]); } } } fn main() { let a:[&[f64];3]=[&[1.1,-0.2, 0.1,1.6], &[0.1,-1.2,-0.2,2.3], &[0.2,-0.1, 1.1,1.5]]; let x:[f64;3]=[0.0;3]; foo(&a,&x); } К сожалению их "книга" пока не богата на примеры передачи параметров в функцию. Кроме собственно официальной документации по Rust существует очень хорошее руководство с множеством прекрасных примеров Rust by Example.

Ответ 2



Можно делать вот так, получается красивее всего: fn foo, Row: AsRef<[f64]>>(a:Matrix,x:&[f64]) { for i in 0..3 { for j in 0..4 { println!("{}",a.as_ref()[i].as_ref()[j]); } } } fn main() { let A:[[f64;4];3]=[[1.1,-0.2, 0.1,1.6], [0.1,-1.2,-0.2,2.3], [0.2,-0.1, 1.1,1.5]]; let mut X:[f64;3]=[0.0;3]; foo(&A,&X); } Но в этом случае длина массивов не должна превышать 32 (потолок для размерности массивов, для которых в компиляторе поддерживается реализация типажей, в числе которых AsRef/AsMut). Ждем реализации параметризации размерностей.

суббота, 22 февраля 2020 г.

Lua, функциональное программирование

#массивы #алгоритм #функции #lua


Дана задача: в массиве чисел (допустим 1 2 3 4 5 6 7 8 9) реверсировать числа на
нечетных позициях (тут это 1,3,5,7,9, должно получится 9 2 7 4 5 6 3 8 1).
Все это сделать с помощью функционального программирования:
Запрещено использовать циклы, условные операторы, операторы присваивания, операторы
контроля (return можно). Все делать с помощью рекурсий и вызовов других функций.

Вот что я набросал:  

local function main()
array = {};
modArray = {};

--Finding count of nums
file = io.open("nums.txt", "r");
numsArrayString = file:read();

stofSpaces = string.gsub(numsArrayString, "%d", "")
ctofNums = string.len(stofSpaces);
file:close();

--Filling array
file = io.open("nums.txt", "r");
fillArray(array, 0);
file:close();

--1st part
irEnd(array, modArray, 0);
end

function fillArray(array, i)
array[i] = file:read("*n");
pos = endOfArray(ctofNums, i) and fillArray(array, i+1);
end

function endOfArray(a, b)
return a >= b;
end

function irEnd(array, modArray, i)
pos = irEndChecker(i+1) and irEnd(array, modArray, i+2);
modArray[i] = array[i];
print(modArray[i]);
end

function irEndChecker(a)
return a < ctofNums;
end

main();  


print(modArray[i]); выводит все в нужном мне порядке (9,7,5,3,1, и четные между ними),
только вот мне не выводить нужно а присваивать (получается 1,3,5,7,9 в нужном массиве).
Что мне сделать с рекурсивной функцией не только для корректного вывода, но и для конкретного
присваивания?
    


Ответы

Ответ 1



Для начала, приведу полный код решения задачи. Сможете убедиться, что эта мешанина скобок даёт нужный результат. Если после прочтения кода он останется непонятым, если явные отсылки к SICP останутся незамеченными, то прошу листать ниже -- там я попытаюсь объяснить, как работает этот оголтелый псевдолисп. -- Создание пары function cons(_x, _y) return function(m) return (m == 0 and _x) or _y end end -- Получаем "голову" списка (первый элемент) function head(list) return (list ~= nil and list(0)) or nil end -- Получаем "хвост" списка (всё кроме первого элемента) function tail(list) return (list ~= nil and list(1)) or nil end -- Собираем список в строку function list2str(list) return (tail(list) ~= nil and head(list) .. ', ' .. list2str(tail(list))) or head(list) end -- Собираем список из параметров функции function make_list(head, ...) return (... ~= nil and cons(head,make_list(...))) or cons(head, nil) end -- Переворачиваем список function reverse_list(list) return reverse_acc( list, nil ) end -- Сервисная функция с аккумулятором для переворота списка function reverse_acc(list, acc) return (tail(list) ~= nil and reverse_acc( tail(list), cons( head(list), acc ) ) ) or cons( head(list), acc ) end -- Получаем каждый второй элемент списка. От index зависит, -- будут ли это чётные или нечётные элементы function get_sublist(list, index) return (tail(list) ~= nil and (index == 1 and cons( head(list), get_sublist( tail(list), 0)) or get_sublist( tail(list), 1) ) ) or (index == 1 and cons(head(list), nil) ) or nil end -- Получаем нечётные елементы списка function get_odd(list) return get_sublist(list, 1) end -- Получаем чётные елементы списка function get_even(list) return get_sublist(list, 0) end -- Собираем в один два списка {a1, a2, ...} и {b1, b2, ...} -- по принципу: {a1, b1, a2, b2, ...} function megre_lists(list1, list2) return (tail(list1) ~= nil and cons( head(list1), cons( head(list2), megre_lists( tail(list1), tail(list2) )))) or cons( head(list1), head(list2) ~= nil and cons( head(list2),nil) or nil) end function main_task(list) -- Печать первоначального списка print( list2str(list) ) -- Тестирование функции переворачивания списка print( list2str(reverse_list(list)) ) -- Тестирование получения нечётных элементов print( list2str(get_odd(list)) ) -- Тестирование получения чётных элементов print( list2str(get_even(list)) ) -- Получаем список нечётных элементов, переворачиваем его и сливаем со списком -- чётных элементов print ( list2str( megre_lists( reverse_list( get_odd(list) ), get_even(list) ) ) ) end main_task( make_list(unpack( {1, 2, 3, 4, 5, 6, 7, 8, 9} )) ) Начинаем разбираться с принципами функционального программирования. В первую очередь, нам нужен истинно функциональный тип данных. То есть, данные мы будем хранить прямо в функции. И да, этот тип данных -- неизменяемый. function cons(_x, _y) return function(m) return (m == 0 and _x) or _y end end Функция возвращает функциональный тип данных, который будем называть «пара». Этот тип данных содержит два значения. Чтобы эти значения мы могли получать, нам нужно ввести ещё две функции: function head(list) return (list ~= nil and list(0)) or nil end function tail(list) return (list ~= nil and list(1)) or nil end Функция head (голова) позволяет получить первый элемент из пары, а функция tail (хвост) – второй элемент. Почему у функций названия «голова» и «хвост» станет понятно далее. Кстати, уже видно как мы будем обходиться без условного оператора. На самом деле, условный оператор нам нужен. Без него из рекурсии не выйти. Поэтому пользуемся удобным синтаксисом lua и совершенно не меняя логику работы вместо if condition then code1 else code2 end пишем так: condition and code1 or code2 С условным оператором разобрались. Продолжаем делать функциональные типы данных. Нам нужны списки для привычной функциональной парадигмы работы с данными. Для этого мы используем функциональную пару. В первый элемент пары мы записываем первый элемент списка, а во второй – вторую пару. Во второй паре у нас опять значение находится в первом элементе, а во втором – следующая пара. Получается своеобразная цепь из пар. Во втором элементе последней пары в цепи будем устанавливать nil. Это будет признаком конца списка. Функция make_list формирует список из своих аргументов. Вот теперь появляется смысл у названий функций head и tail. Первая функция возвращает первый элемент списка, а вторая – весь остальной список. Как голова удава, и его хвост. Если вы не знакомы с функциональным программированием, то просто поверьте на слово – трёх функций cons, head, tail вполне достаточно для любых работ с функциональными списками. А дальше мы решаем все задачи при помощи функций с рекурсиями. Это как нарисовать сову. Это как на лекции по математике лектор говорит: "отсюда легко видеть". Всё скучно и логично. Скучно для тех, кто знаком с базовыми алгоритмами функционального программирования. Остальных я отсылаю к SICP или любой другой книге по функциональному программированию.

пятница, 21 февраля 2020 г.

Почему родительский объект не приводится к типу дочернего?

#cpp #функции #наследование


1) Пытаюсь запустить такой код:

#include 

struct A
{
    virtual void m() {
        std::cout << "A" ;
    }
};

struct B : public A
{
    void m() {
        std::cout << "B";
    }
};

int main()
{
    A a;
    B *b;

    b = &(B)a;
    b->m();

    return 0;
}


Если в main прописать:

int main()
{
    A *a;
    B b;

    a = &b;
    a->m();

    return 0;
}


то все ожидаемо работает. Но вот вопрос, почему нельзя привести тип родителя к типу
наследника, а наоборот можно? Как здесь работает механизм приведения типов? Или все
же можно привести тип родителя к типу наследника?

1) Еще такой вопрос, если написать так:

struct A
{
     void m() {
        std::cout << "A" ;
    }
};

struct B : public A
{
    void m() {
        std::cout << "B";
    }
};

int main()
{
    A *a;
    B b;

    a = &b;
    a->m();

    return 0;
}


то вызывается m() у обьекта A. Но объекта А как бы не существует. Мне не понятно,
где же тогда вызывается эта m(), у какого объекта типа A если обьекта типа A не существует?
    


Ответы

Ответ 1



Дочерний объект содержит в себе родительский объект, поэтому приведение дочернего объекта к родительскому объекту не вызывает проблем и может быть осуществлено неявно. С другой стороны, родительский объект ничего не знает о том, какие и как могут быть определены дочерние от него объекты. Он такой информацией не обладает. А потому нет неявного преобразования из родительского объекта в какой-либо дочерний. Когда нет виртуальных функций, то происходит статическое связывание вызываемых функций с типом объекта на этапе компиляции. В этой программе #include struct A { void m() { std::cout << "A" ; } }; struct B : public A { void m() { std::cout << "B"; } }; int main() { A *a; B b; a = &b; a->m(); return 0; } статическим типом указателя a является тип struct A, поэтому компилятор связывает функцию, объявленную в этом типе. На самом деле то же самое происходит и для виртуальных функций, то есть компилятор осуществляет поиск имени функции в соответствии со статическим типом указателя или ссылки на объект. Другое дело, что вызов функции осуществляется с помощью механизма, использующего таблицу виртуальных функций. Адрес этой таблицы разрешается динамически на этапе выполнения программы, хотя в некоторых простых случаях, когда статический и динамический типы совпадают, то может это делать на этапе компиляции.

Ответ 2



привидение родителя к наследнику выполнять разрешается и это называется downcast, которое можно делать, например, так b = static_cast(&a); А в вашем первом примере будет ошибка компиляции ввиду некорректного с точки зрения синтаксиса языка С++ кода b = &(B)a; вероятно, вы хотели написать так b = (B*)&a; edit ошибка компиляции будет, как отметил Ant, не с точки зрения синтаксиса а с точки зрения семантики языка С++ это каст в стиле С - которого лучше избегать, а в случае с downcast подавно - предпочтительнее применять специальные и более безопасные операторы явного приведения типов из С++ (как написано выше до этого) А вобще, подобный downcast в некоторых случаях может приводить к неопределённому поведению по поводу третьего вопроса - выводится A так как эта функция в родительском классе не виртуальна - в этом и основной смысл виртуальных функий - обеспечение вызова переопределённого в наследнике родительского метода через указатель на базовый класс (родительский класс), соответственно если в базовом классе функция не виртуальна то он и вызовется

Ответ 3



Спасибо, ampawd! Я прозрел! Код ниже выводит ВВ, как и типа ожидалось #include class A {public: virtual void m() { std::cout << "A" ; } }; class B : public A {public: void m() { std::cout << "B"; } }; int main() { A *a; B b; a = &b; a->m(); b.m(); return 0; }

воскресенье, 16 февраля 2020 г.

Lua, функциональное программирование

#массивы #алгоритм #функции #lua


Дана задача: в массиве чисел (допустим 1 2 3 4 5 6 7 8 9) реверсировать числа на
нечетных позициях (тут это 1,3,5,7,9, должно получится 9 2 7 4 5 6 3 8 1).
Все это сделать с помощью функционального программирования:
Запрещено использовать циклы, условные операторы, операторы присваивания, операторы
контроля (return можно). Все делать с помощью рекурсий и вызовов других функций.

Вот что я набросал:  

local function main()
array = {};
modArray = {};

--Finding count of nums
file = io.open("nums.txt", "r");
numsArrayString = file:read();

stofSpaces = string.gsub(numsArrayString, "%d", "")
ctofNums = string.len(stofSpaces);
file:close();

--Filling array
file = io.open("nums.txt", "r");
fillArray(array, 0);
file:close();

--1st part
irEnd(array, modArray, 0);
end

function fillArray(array, i)
array[i] = file:read("*n");
pos = endOfArray(ctofNums, i) and fillArray(array, i+1);
end

function endOfArray(a, b)
return a >= b;
end

function irEnd(array, modArray, i)
pos = irEndChecker(i+1) and irEnd(array, modArray, i+2);
modArray[i] = array[i];
print(modArray[i]);
end

function irEndChecker(a)
return a < ctofNums;
end

main();  


print(modArray[i]); выводит все в нужном мне порядке (9,7,5,3,1, и четные между ними),
только вот мне не выводить нужно а присваивать (получается 1,3,5,7,9 в нужном массиве).
Что мне сделать с рекурсивной функцией не только для корректного вывода, но и для конкретного
присваивания?
    


Ответы

Ответ 1



Для начала, приведу полный код решения задачи. Сможете убедиться, что эта мешанина скобок даёт нужный результат. Если после прочтения кода он останется непонятым, если явные отсылки к SICP останутся незамеченными, то прошу листать ниже -- там я попытаюсь объяснить, как работает этот оголтелый псевдолисп. -- Создание пары function cons(_x, _y) return function(m) return (m == 0 and _x) or _y end end -- Получаем "голову" списка (первый элемент) function head(list) return (list ~= nil and list(0)) or nil end -- Получаем "хвост" списка (всё кроме первого элемента) function tail(list) return (list ~= nil and list(1)) or nil end -- Собираем список в строку function list2str(list) return (tail(list) ~= nil and head(list) .. ', ' .. list2str(tail(list))) or head(list) end -- Собираем список из параметров функции function make_list(head, ...) return (... ~= nil and cons(head,make_list(...))) or cons(head, nil) end -- Переворачиваем список function reverse_list(list) return reverse_acc( list, nil ) end -- Сервисная функция с аккумулятором для переворота списка function reverse_acc(list, acc) return (tail(list) ~= nil and reverse_acc( tail(list), cons( head(list), acc ) ) ) or cons( head(list), acc ) end -- Получаем каждый второй элемент списка. От index зависит, -- будут ли это чётные или нечётные элементы function get_sublist(list, index) return (tail(list) ~= nil and (index == 1 and cons( head(list), get_sublist( tail(list), 0)) or get_sublist( tail(list), 1) ) ) or (index == 1 and cons(head(list), nil) ) or nil end -- Получаем нечётные елементы списка function get_odd(list) return get_sublist(list, 1) end -- Получаем чётные елементы списка function get_even(list) return get_sublist(list, 0) end -- Собираем в один два списка {a1, a2, ...} и {b1, b2, ...} -- по принципу: {a1, b1, a2, b2, ...} function megre_lists(list1, list2) return (tail(list1) ~= nil and cons( head(list1), cons( head(list2), megre_lists( tail(list1), tail(list2) )))) or cons( head(list1), head(list2) ~= nil and cons( head(list2),nil) or nil) end function main_task(list) -- Печать первоначального списка print( list2str(list) ) -- Тестирование функции переворачивания списка print( list2str(reverse_list(list)) ) -- Тестирование получения нечётных элементов print( list2str(get_odd(list)) ) -- Тестирование получения чётных элементов print( list2str(get_even(list)) ) -- Получаем список нечётных элементов, переворачиваем его и сливаем со списком -- чётных элементов print ( list2str( megre_lists( reverse_list( get_odd(list) ), get_even(list) ) ) ) end main_task( make_list(unpack( {1, 2, 3, 4, 5, 6, 7, 8, 9} )) ) Начинаем разбираться с принципами функционального программирования. В первую очередь, нам нужен истинно функциональный тип данных. То есть, данные мы будем хранить прямо в функции. И да, этот тип данных -- неизменяемый. function cons(_x, _y) return function(m) return (m == 0 and _x) or _y end end Функция возвращает функциональный тип данных, который будем называть «пара». Этот тип данных содержит два значения. Чтобы эти значения мы могли получать, нам нужно ввести ещё две функции: function head(list) return (list ~= nil and list(0)) or nil end function tail(list) return (list ~= nil and list(1)) or nil end Функция head (голова) позволяет получить первый элемент из пары, а функция tail (хвост) – второй элемент. Почему у функций названия «голова» и «хвост» станет понятно далее. Кстати, уже видно как мы будем обходиться без условного оператора. На самом деле, условный оператор нам нужен. Без него из рекурсии не выйти. Поэтому пользуемся удобным синтаксисом lua и совершенно не меняя логику работы вместо if condition then code1 else code2 end пишем так: condition and code1 or code2 С условным оператором разобрались. Продолжаем делать функциональные типы данных. Нам нужны списки для привычной функциональной парадигмы работы с данными. Для этого мы используем функциональную пару. В первый элемент пары мы записываем первый элемент списка, а во второй – вторую пару. Во второй паре у нас опять значение находится в первом элементе, а во втором – следующая пара. Получается своеобразная цепь из пар. Во втором элементе последней пары в цепи будем устанавливать nil. Это будет признаком конца списка. Функция make_list формирует список из своих аргументов. Вот теперь появляется смысл у названий функций head и tail. Первая функция возвращает первый элемент списка, а вторая – весь остальной список. Как голова удава, и его хвост. Если вы не знакомы с функциональным программированием, то просто поверьте на слово – трёх функций cons, head, tail вполне достаточно для любых работ с функциональными списками. А дальше мы решаем все задачи при помощи функций с рекурсиями. Это как нарисовать сову. Это как на лекции по математике лектор говорит: "отсюда легко видеть". Всё скучно и логично. Скучно для тех, кто знаком с базовыми алгоритмами функционального программирования. Остальных я отсылаю к SICP или любой другой книге по функциональному программированию.

Почему родительский объект не приводится к типу дочернего?

#cpp #функции #наследование


1) Пытаюсь запустить такой код:

#include 

struct A
{
    virtual void m() {
        std::cout << "A" ;
    }
};

struct B : public A
{
    void m() {
        std::cout << "B";
    }
};

int main()
{
    A a;
    B *b;

    b = &(B)a;
    b->m();

    return 0;
}


Если в main прописать:

int main()
{
    A *a;
    B b;

    a = &b;
    a->m();

    return 0;
}


то все ожидаемо работает. Но вот вопрос, почему нельзя привести тип родителя к типу
наследника, а наоборот можно? Как здесь работает механизм приведения типов? Или все
же можно привести тип родителя к типу наследника?

1) Еще такой вопрос, если написать так:

struct A
{
     void m() {
        std::cout << "A" ;
    }
};

struct B : public A
{
    void m() {
        std::cout << "B";
    }
};

int main()
{
    A *a;
    B b;

    a = &b;
    a->m();

    return 0;
}


то вызывается m() у обьекта A. Но объекта А как бы не существует. Мне не понятно,
где же тогда вызывается эта m(), у какого объекта типа A если обьекта типа A не существует?
    


Ответы

Ответ 1



Дочерний объект содержит в себе родительский объект, поэтому приведение дочернего объекта к родительскому объекту не вызывает проблем и может быть осуществлено неявно. С другой стороны, родительский объект ничего не знает о том, какие и как могут быть определены дочерние от него объекты. Он такой информацией не обладает. А потому нет неявного преобразования из родительского объекта в какой-либо дочерний. Когда нет виртуальных функций, то происходит статическое связывание вызываемых функций с типом объекта на этапе компиляции. В этой программе #include struct A { void m() { std::cout << "A" ; } }; struct B : public A { void m() { std::cout << "B"; } }; int main() { A *a; B b; a = &b; a->m(); return 0; } статическим типом указателя a является тип struct A, поэтому компилятор связывает функцию, объявленную в этом типе. На самом деле то же самое происходит и для виртуальных функций, то есть компилятор осуществляет поиск имени функции в соответствии со статическим типом указателя или ссылки на объект. Другое дело, что вызов функции осуществляется с помощью механизма, использующего таблицу виртуальных функций. Адрес этой таблицы разрешается динамически на этапе выполнения программы, хотя в некоторых простых случаях, когда статический и динамический типы совпадают, то может это делать на этапе компиляции.

Ответ 2



привидение родителя к наследнику выполнять разрешается и это называется downcast, которое можно делать, например, так b = static_cast(&a); А в вашем первом примере будет ошибка компиляции ввиду некорректного с точки зрения синтаксиса языка С++ кода b = &(B)a; вероятно, вы хотели написать так b = (B*)&a; edit ошибка компиляции будет, как отметил Ant, не с точки зрения синтаксиса а с точки зрения семантики языка С++ это каст в стиле С - которого лучше избегать, а в случае с downcast подавно - предпочтительнее применять специальные и более безопасные операторы явного приведения типов из С++ (как написано выше до этого) А вобще, подобный downcast в некоторых случаях может приводить к неопределённому поведению по поводу третьего вопроса - выводится A так как эта функция в родительском классе не виртуальна - в этом и основной смысл виртуальных функий - обеспечение вызова переопределённого в наследнике родительского метода через указатель на базовый класс (родительский класс), соответственно если в базовом классе функция не виртуальна то он и вызовется

Ответ 3



Спасибо, ampawd! Я прозрел! Код ниже выводит ВВ, как и типа ожидалось #include class A {public: virtual void m() { std::cout << "A" ; } }; class B : public A {public: void m() { std::cout << "B"; } }; int main() { A *a; B b; a = &b; a->m(); b.m(); return 0; }

пятница, 14 февраля 2020 г.

Возвращение массива функцией

#cpp #массивы #функции #динамические_массивы


Есть код. В функции double f(double x[], int n) делается вычисление нескольких функций
с несколькими переменными. Несколько переменных мы передадим в виде массива double
x[], int n - количество элементов массива; к примеру, пусть будут вычислятся две функции
- я их решил записать в массив double g[n], так как мне надо, что бы функция возвращала
оба вычисления функции: g[0] = x[0] + x[1]; g[1] = x[0] * x[1]. Как вернуть массив
g[n], и каким быть должно обращение к функции (в примере кода сразу в функции memcpy)?
Что не так, поправьте. 
Пример кода.

#include 

using namespace std;

double f(double x[], int n) 
{
    double g[n];
    g[0] = x[0] + x[1];
    g[1] = x[0] * x[1];
    return /* Что? */;
}

main() 
{
    int n = 2;
    double a[2], b[2];
    for (int i = 0; i < n; i++)
    {
        cout << " a[" << i << "] = ";
        cin >> a[i];
    }
    memcpy(b, f(a, n), sizeof(a));
    for (int i = 0; i < n; i++) cout << " b[" << i << "] = " << b[i];
    return 0;
}



    


Ответы

Ответ 1



Возвращать массив как таковой нельзя. Можно вернуть указатель на него - но тогда массив не должен быть локальной переменной! (Кстати, в C++ нельзя объявлять массив с размером, неизвестным во время компиляции.) Поэтому нужно иначе.. Выделить массив динамически, и вернуть (только потом не забыть освободить память!) int * g = new int[n]; ... return g; Использовать, скажем, вектор (наилучший вариант): std::vector g(n); ... return g; Можно обернуть массив в структуру, но опять же, нужно знать размер во время компиляции: struct Array { int g[10]; } ... Array f() { Array g; g.g[0] = 5; ... return g; } Но лучше всего - использовать готовый вектор, тем более что у вас количество элементов заранее не известно, так что std::array<> вам не подойдет. PS Ну и, конечно, вариант с передачей возвращаемого массива в функцию: int g[10]; ... f(..., int* g) { // работа с g[i] ... } ... f(...,g);