LINUX.ORG.RU

Итератор из функции

 


1

3
#include <stdio.h>

void func()
{
	for (int i = 1; i <= 4; i++) {
		printf("%d\n", i);
	}
}

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

Можно это переделать, чтобы использование func() было в стиле

int main()
{
	while ((i = func()) != NULL) {
		printf("%d\n", i);
	}
	return 0;
}

без сохранения всего результата выполнения for{} в память?


Так что ли?

#include <stdio.h>

int
func()
{
        static int n = 0;

        return n++;
}

int
main()
{
        int i;

        while ((i = func()) < 4)
                printf("%d\n", i);

        return 0;
}

beastie ★★★★★
()
Ответ на: комментарий от beastie

Пример с for (int i - это упрощение. Допустим, func() читает построчно файл и выбирает элементы каким-то образом. Или обходит рекурсивно директорию, выдавая список файлов. Сложить все в массив, для дальнейшего использования - не хватит памяти.

slendy
() автор топика

Можешь использовать замыкания (или блоки, как они называются в C или smalltalk)

#include <stdio.h>
#include <Block.h>

static void *fuck ()
{
    __block int n = 0;
    int (^block)() = ^{
        int res = n;
        n++;
        if (res < 4) return res;
        else return -1;
    };
    int (^copy)() = Block_copy (block);
    return copy;
}

int main ()
{
    int (^block)() = fuck();
    int res;

    while ((res = block()) != -1)
        printf ("%i\n", res);

    Block_release (block);
    return 0;
}
anonymous
()
Ответ на: комментарий от anonymous

Можно так

#include <stdio.h>
#include <Block.h>

static void *count_to (int n)
{
    __block int i = 0;
    int (^block)() = ^{
        int res = i;
        i++;
        if (res < n) return res;
        else return -1;
    };
    int (^copy)() = Block_copy (block);
    return copy;
}

int main ()
{
    int (^block)() = count_to(10);
    int res;

    while ((res = block()) != -1)
        printf ("%i\n", res);

    Block_release (block);
    return 0;
}

Правило такое. Переменные из лексического окружения захватываются по значению и значение в теле блока им присвоить нельзя. В переменные из лексического окружения с спецификатором __block можно писать внутри блока. Касательно сложных данных в виде массивов и структур это может и не работать. Блок создается на стеке и живет внутри скобочек {} где был создан. Блок можно скопировать в кучу с помощью Block_copy() и освободить с помощью Block_release(). При этом переменные с спецификатором __block тоже копируются.

anonymous
()
Ответ на: комментарий от slendy

Тут вообще всё внутреннее состояние хранится в ядре, а у тебя есть дескриптор к файлу. Ну и читай из файла, возвращай что тебе надо по 1 элементу. А когда надо простой ленивый генератор, то можно сохранить состояние в блоках как я показал

anonymous
()
Ответ на: комментарий от anonymous

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

Давай какой-то местный «эксперт» не будет рассуждать, что нужно, а что не нужно.

Как пример этот синтаксис использует библиотека GCD, которая реализует параллельные вычисления на основе очередей в C. А что мы имеем для параллелизма в C на Linux? Жалкий openmp, который работает на прагмах и может «распараллелить цикл». Для остального С без GCD не пригоден.

Как вариант можно было бы и как нестандартное расширение к pthreads использовать блоки для создания потоков

anonymous
()
Ответ на: комментарий от anonymous

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от slendy

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

anonymous
()
Ответ на: комментарий от Iron_Bug

Лол, ты какую-то ахинею несёшь. Расплодили вообще каких-то C. Зачем, ведь есть ассемблер?

Вот блоки это реально, чего не хватает в обычных библиотеках. Колбеки все используют? Все. А блоки были бы здесь идеальны.

Хотя бы для быстрой сортировки. Вместо сразу 2 функций: qsort и qsort_r

anonymous
()
Ответ на: комментарий от anonymous

ты жопу с пальцем не путай. язык программирования ничего не должен параллелизму. параллелизм - это свойство ОС и интефейсы для его использования предоставляются осью, а никак не ЯП. ручками через потоки он делается в Линюксе. а если руки не оттуда растут, то это не проблема С. и библиотеки тут не помогут.

и ещё имеет смысл отличать CPU от GPU и принципы их работы, однако.

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

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 1)
Ответ на: комментарий от anonymous

Зачем, ведь есть ассемблер?

ассемблер хорош иногда, если к месту применяется. ничего против него не имею. кстати, в С легко делаются ассемблерные вставки.

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

ты жопу с пальцем не путай. язык программирования ничего не должен параллелизму. параллелизм - это свойство ОС и интефейсы для его использования предоставляются осью, а никак не ЯП

Я тебе привел пример просто того, что без блоков выглядело бы убого.

ручками через потоки он делается в Линюксе. а если руки не оттуда растут, то это не проблема С. и библиотеки тут не помогут.

GCD тоже работает «через потоки», не через pthreads, правда. И что?

и ещё имеет смысл отличать CPU от GPU и принципы их работы, однако.

Это к чему вообще сказано?

вышеозначенная фича - типичнейший пример эталонного ненужно в С. чистый синтаксический сахар, который существенно портит читабельность

Ну я говорю, давай лороэксперты не будут тут кудахтать. «Во имя Кернигана Ричи!! НЕ НУЖНО!!111». Попиши код лучше, может тогда понимать будешь

anonymous
()
Ответ на: комментарий от anonymous

Я тебе привел пример просто того, что без блоков выглядело бы убого.

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от anonymous

я всё помню. и где там callback? в С такого понятия нет. в твоей википедии написано именно про указатель. см. мой пост выше.

Iron_Bug ★★★★★
()
Ответ на: комментарий от slendy

Тогда смотри на пример и задавай вопросы. Потому что этот пример - единственное, что посоветовали годного.

tailgunner ★★★★★
()
Ответ на: комментарий от Iron_Bug

язык программирования ничего не должен параллелизму. параллелизм - это свойство ОС и интефейсы для его использования предоставляются осью, а никак не ЯП

Расскажи это Occam и Ada.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

ну дык не надо их с сишечкой сравнивать

Почему бы и не сравнить, после такого глобального заявления:

Iron_Bug> язык программирования ничего не должен параллелизму

С не реализует конкуррентное выполнение, потому что это язык, на котором сами оси пишут

На Occam тоже писали ОС.

и в нём системные вызовы нельзя встроить в язык

Системные вызовы не обязательны для реализации параллелизма.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Файл лежит на диске, и для него есть fseek(). Но если элемент i получается в результате неких вычислений, количество элементов заранее не известно. Как отмечать текущую позицию, и продолжать с нее, не начиная все заново?

Если не сильно геморрно, хочется рабочий пример. А то я полез в мьютексы и корутины и, кажется, куда-то не туда думаю.

slendy
() автор топика
Ответ на: комментарий от slendy

Ты напиши нормально и подробно чего тебе надо, пока что ничерта не понятно. Может тебе глобальную переменную завести просто?

anonymous
()
Ответ на: комментарий от slendy

Если не сильно геморрно, хочется рабочий пример

Непонятно, что тебе надо.

struct iter_state {
  int *pos, *end;
};

void init_iter(struct iter_state *s, int *arr, int len)
{
  s->pos = arr;
  s->end = s->pos + len;
}

int *next(struct iter_state *s)
{
  if (s->pos != s->end)
    return s->pos++;
  else
    return NULL;
}

void main()
{
  int a[10];
  struct iter_state i;
  int *elt;

  init_iter(&i, a, 10);
  while((elt = next(i)) != NULL)
    printf("%d\n", *elt);
}

я полез в мьютексы и корутины и, кажется, куда-то не туда думаю.

Определенно.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Почему бы и не сравнить, после такого глобального заявления

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

Системные вызовы не обязательны для реализации параллелизма.

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от anonymous

Ну, если помечтать, получить итератор из функции в общем случае.

Предметно - делал, не на Си правда, такую штуку. Функция1 рекурсивно обходит каталог и делает на файлы libmagic. Для нужных файлов вызывается функция2, которая считывает файл по частям, используя заданный байт, как разделитель. Полученные байт-строки передаются для обработки функции3.

Вот от таких вложенностей и хочу избавится.

main() {
	функция1()
}
функция1() {
	функция2() {
		функция3() {
			
		}
	}
}
main() {
	функция1()
		функция2()
			функция3()
}
функция1() {
	
}
функция2() {
	
}
функция3() {
	
}
slendy
() автор топика
Ответ на: комментарий от Iron_Bug

нельзя писать систему на языке, который эту же систему сам дёргает

Это и не нужно.

Системные вызовы не обязательны для реализации параллелизма.

да ну?

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

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

Сколько нерелевантных вопросов.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

GCD тащемта почти везде имеет <funcname>_f(..., void *, dispatch_function_t) варианты, так что ей можно пользоваться и без блоков. Блоки это не более, чем синтаксический сахар для Итератор из функции (комментарий). /thread

arturpub ★★
()
Ответ на: комментарий от tailgunner

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от slendy

fread/read например и есть «итераторы», а FILE/int это их стейты. Нафига тебе там fseek, если рид-поинтер и так сам по себе двигается? Примеров в сети миллиарды. Но если так надо, вот факториал-итератор.

struct state { int n; double v; }
static double factiter(struct state *st) { return st->v *= ++st->n; }

struct state st = { 0, 1 };
double x;
while (isfinite((x = factiter(&st)))) { ... }

Как отмечать текущую позицию, и продолжать с нее, не начиная все заново

Сохранять в стейт при выходе, использовать при следующем входе.

arturpub ★★
()
Ответ на: комментарий от anonymous

Почти всегда, именно так и делаю. Плюс еще передача функции в другую, как параметр. Но иногда, это все начинает разрастаться.

slendy
() автор топика
Ответ на: комментарий от Iron_Bug

и конечно, всякие компиляторы прямо вот «изнутри ядра» управляют выполнением приложухи

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

tailgunner ★★★★★
()
Ответ на: комментарий от slendy

Но иногда, это все начинает разрастаться.

«Структурирувай» (передавай в виде (указателя на структуру) записи активации). - т.е. таки на переносимом ассемблере приходится явно сообщать функции(изображающей «функтор»)её контекст(и да Ричи и Ко сознательно не стали впиливать такую тогда уже распространёную вещь как синтаксическую вложенность обьявлений функций от которой рукой(если менеджить состояния исполнения не на стеке а в куче) до замыканий)

qulinxao ★★☆
()
Ответ на: комментарий от anonymous

Убого выглядит fuck() с Block_copy() и __block, когда можно было просто выделить стейт на вызывающем стеке. Но тут уж кому что.

arturpub ★★
()
Ответ на: комментарий от slendy

Почти всегда, именно так и делаю.

пора подумать о логике приложения, а не о костылях

unt1tled ★★★★
()
Ответ на: комментарий от RiseOfDeath

Знаешь, c-faq я верю как-то больше, чем умникам с ЛОРа. ;)

TL;DR:
int func() — (устаревшее, но всё ещё верное) I don't care about my arguments
int func(void) — no arguments at all

И я об этом знаю, но это по большей части абсолютно не принципиально.

beastie ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.