FreeBasic
Главная
Вход
Регистрация
Суббота, 21.12.2024, 20:01Приветствую Вас Гость | RSS
[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Предложение отправить issue по поводу цикла for
electrikДата: Суббота, 20.05.2023, 03:42 | Сообщение # 1
Полковник
Группа: Друзья
Сообщений: 182
Репутация: 3
Статус: Offline
Всем часто приходится работать с циклом for. Некоторые занимаются адаптацией программ с других языков.
как вот вам такая конструкция, ещё не надоела?

Код
for i = 1 to size-1


Под size, подразумевается не константа, а переменная.
конечно можно сделать так:

Код
while i < size
i +=1
wend


А если это цикл, в котором сложное ветвление с continue! Как мы знаем, что только в цикле for continue делает инкремент переменной. Идея понятна? в сложных ветвлениях просто можно забыть сделать инкремент.
Поэтому, в некоторых случаях for всё же лучше. А теперь представим вложенные циклы в которых идёт бессмысленный декремент на единицу. Да и опять таки, при переписывании кода, по запарке можно забыть этот декремент вписать.
Непонятно, почему в бейсиках этот вопрос до сих пор не решён.
Вопрос, какой синтаксис им предложить? а вот тут даже не знаю. Ну скажем:

Код
for i = 1 to < size


Может странно выглядит. Можно иначе, но это уже не будет увязываться с константами:

Код
for i = 1 to size<10


Ну а если я захочу написать:

Код
for i = 1 to 10 < 10


Стрёмненько выглядит.
Первый вариант с константами будет красивей:

Код

for i = 1 to < 10
/code]
Стоит ли что-то менять, или пусть бейсик будет как есть?
Мне кажется, что некоторые фишки добавлять нужно, ибо бейсик уже не для обучения в школе. В школьных задачах действительно проще писать в лоб от 1 до 10, но бейсик уже не школьник.
Короче, какие ещё придумаете конструкции, которые не будут выбиваться из синтаксиса бейсика?
 
haavДата: Суббота, 20.05.2023, 14:49 | Сообщение # 2
Генералиссимус
Группа: Администраторы
Сообщений: 1373
Репутация: 50
Статус: Offline
Это же почти аналог сишной конструкции FOR. Вряд ли кому-то это придется по вкусу. Хотя , кто знает.

Вы сохраняете власть над людьми покуда оставляете им что-то…Отберите у человека все, и этот человек уже будет неподвластен вам…
 
zamabuvaraeuДата: Вторник, 23.05.2023, 07:00 | Сообщение # 3
Подполковник
Группа: Друзья
Сообщений: 149
Репутация: 5
Статус: Offline
Я против.
Язык уже итак раздут дублирующими и неоднозначными конструкциями, и должен быть только один способ получения результата.
 
ntvgjhfnjДата: Вторник, 23.05.2023, 18:37 | Сообщение # 4
Лейтенант
Группа: Проверенные
Сообщений: 61
Репутация: 1
Статус: Offline
Цитата "zamabuvaraeu" ()
Язык уже итак раздут дублирующими и неоднозначными конструкциями, и должен быть только один способ получения результата.

Способы нужны разные или один ,но универсальный.


polopok
 
sashasoldДата: Среда, 24.05.2023, 19:14 | Сообщение # 5
Лейтенант
Группа: Пользователи
Сообщений: 41
Репутация: 0
Статус: Offline
"for i = 1 to size-1"
Чтобы не сбится, и для некой оптимизации я пишу
predel = size-1
for i = 1 to predel
'Тело
Next

А если много ветвлений, вложенных циклов, то может лучше
i=0
Do until i=predel
i+=1
'Тело
Loop

Do loop вообще универсальней, а с подменой на predel код быстрее, при большом количестве повторений

Добавлено (25.05.2023, 00:02)
---------------------------------------------
Да и еще если применять сложные условия и разрыв цикла
For i=1 to abv
If a=b then exit for
Next
Отслеживаем значение i

Интересно, что если цикл прерывается при exit for то значение i ожидаемо
А вот если цикл завершается полностью, то i не будет равно abv как ожидается, а будет abv+1.
Так что видимо цикл for next, реализоан внутри, через ограничение i>abv

Так, вспомнился нюанс)


Сообщение отредактировал sashasold - Четверг, 25.05.2023, 00:03
 
electrikДата: Вторник, 13.06.2023, 23:31 | Сообщение # 6
Полковник
Группа: Друзья
Сообщений: 182
Репутация: 3
Статус: Offline
Да петлёй то можно, но как я говорил, случаи бывают всякие, и при очередном continue можно не добавить счётчик. Синтаксис менять и не надо, просто нужно добавить новые конструкции.
Можно и универсальный while сделать, к примеру, шлёпнуть макросы endwhile и continuewhile, которые будут всё прибавлять где надо, но это костыли в рамках своих проектов. А если мне нужно прибавлять не единицу а другое число, вообще неизвестное из переменной, короче это хрень.
Я переписываю одну программу с C, так там for и с кучей continue. Как я долго матерился когда искал ошибку, а всё вина в том, что там замороченый for, который можно переписать только используя while, и я конечно же после continue забыл заинкрементить переменную:

Код

  for (i = 0; (i < (sizeof(clause_terminations) / sizeof(uint16_t))) && (clause_terminations [i]!= termination); i++)[/i]


Короче for с несколькими условиями. Такое на бейсике можно сделать только при помощи while.

Добавлено (31.08.2023, 19:19)
---------------------------------------------
Вот что я попытался намудрить на макросах, в C стиле, но оно не будет или не правильно будет работать в случае вложенных циклов.
Изначальный код был с оф форума, но они сразу написали что это только для невложенных циклов.
пример первый:

Код
#macro CFOR(startexpr,condexpr,stepexpr)
startexpr
while condexpr
#ifdef CNEXT
#undef CNEXT
#endif
#ifndef CNEXT
#macro CNEXT?()
stepexpr
wend
#endmacro
#endif
#endmacro

dim i as integer
CFOR(i=0,i<5,i+=1)
print "i=",i
cfor(dim j as integer =0,j<10,j+=1)
print "j=", j
CNEXT
CNEXT
/code]
Пример второй:

#macro CFOR(startexpr,condexpr,stepexpr)
startexpr
while condexpr
#ifndef CNEXT
#macro CNEXT?()
stepexpr
wend
#endmacro
#endif
#endmacro

dim i as integer
CFOR(i=0,i<5,i+=1)
print "i=",i
cfor(dim j as integer =0,j<10,j+=1)
print "j=", j
CNEXT
CNEXT

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

dim i as integer
 i=0
while i<5
print "i=",i
 dim j as integer =0
while j<10
print "j=", j
 j+=1
wend
CNEXT

Последний макрос не развернулся, но оно и понятно, он отменен при помощи #undef.
Развёрнутый второй код рабочий но неправильно:

dim i as integer
 i=0
while i<5
print "i=",i
 dim j as integer =0
while j<10
print "j=", j
 i+=1
wend
 i+=1
wend


В данном примере, при повторном вызове cfor, макрос cnext, продолжает отдавать параметры как бы от первого вызова cfor. В первом примере, мы его андефайним, тогда конец второго цикла указывает на правильную переменную, а поскольку макроса больше нет, мы не можем закрыть первый цикл.
Пока тут нет CCONTINUE, но это пару строчек шлёпнуть.
Пока ничего умного в голову не пришло, чтобы всё это работало во вложенных циклах, и не по извращенски.
В макросахх нужно делать или свои #scope блоки, или стек макросов или параметров.
Они уже потихоньку высовывают наружу потроха компилятора, но не быстро.
Офигенную вещь придумали, такую как уникальные имена, я даже попытался это как-то прикрутить к данным макросам, но оно не так работает, как я предполагал.
Стек параметров, или макросов было бы круто. Да, оно будет при компиляции жрать ресурсы и скорость, но это  сразу бы сильно развязало руки.
Выглядело бы это так: __FB_PARAM_PUSH(macroname,param)
Ну или как-то так.
Таким образом, вообще ненужны были бы вложенные макросы, а просто их параметры хранились бы в стеке, а лучше в хэше, и ты мог бы использовать параметры любого, подчёркиваю, вызванного макроса, доставая их из стека или хэша.
Да, и без scope будет не обойтись, потому что одноимённых вложенных названий может быть много, а это duplicate definition.
Если нужно достать произвольные параметры, лучше использовать хеши, тогда вообще не нужно будет думать сколько взял и сколько положил.
Но будет условие, брать и класть ты можешь в любом порядке, но если сколько положил, значит после использования столько освободи.
Незнаю, что будет с компилятором, если кто-то нашлёпает макросов, как шаблонов в boost, и как он будет долго компилировать, но это, как говориться, можно пояснить, что злоупотреблять не нужно.
Вообще давно пора инлайн функции, вот где сила.
давно  считается, что макросы - это рассадник трудноотлавливаемых ошибок. Согласен, костыльно, но правильно написанное работает быстро.


Сообщение отредактировал electrik - Вторник, 13.06.2023, 23:34
 
zamabuvaraeuДата: Воскресенье, 03.09.2023, 22:55 | Сообщение # 7
Подполковник
Группа: Друзья
Сообщений: 149
Репутация: 5
Статус: Offline

Цитата
Короче for с несколькими условиями. Такое на бейсике можно сделать только при помощи while.
Нет. Можно через For.
Оператор For — это не просто какая‐то команда, выражение или ключевое слово, это настоящий оператор, который можно переопределить.
Для начала определим структуру, которая будет выражать наш переопределённый For:

Код
Type CycleCounter
   Declare Constructor( ByVal n As Integer )
   ' Переопределение для неявного использования
   Declare Operator For ( )
   Declare Operator Step( )
   Declare Operator Next( ByRef end_cond As CycleCounter ) As Integer
   
   ' Счётчик
   i As Integer
End Type

Я не знаю что такое clause_terminations, но полагаю что это какая‐то строка. Используем переопределённый For. В качестве счётчика цикла будет использоваться наша структура CycleCounter:

Код
Dim Shared clause_terminations As ZString * 265 = "Hello world!"
Dim Shared termination As Integer = 0

For i As CycleCounter = 0 To 100
   If clause_terminations[i.i] = 32 Then
      ' Теперь можно делать Continue
      ' и счётчик не забудет обновлять сам себя
      Continue For
      ' это просто для примера
      ' чтобы не печатать символы с пробелом
   End If
   ' печатаем текущее значение счётчика и символ из строки
   ' для самоуспокоения
   Print i.i, clause_terminations[i.i]
Next


Ну а теперь реализация оператора For

Код
Constructor CycleCounter( ByVal n As Integer )
   ' Конструктор, просто инициализируемся
   i = n
End Constructor

Operator CycleCounter.For( )
   Print "implicit step"
End Operator

Operator CycleCounter.Step( )
   ' Эта функция будет вызываться для приращения счётчика
   ' по умолчанию приращиваем счётчик на единицу
   this.i += 1
End Operator

Operator CycleCounter.Next( ByRef end_cond As CycleCounter ) As Integer
   ' Эта функция вызывается для проверки окончания цикла
   
   ' Параметр end_cond — это то, что стоит в выражении To
   ' то есть мы должны сравнить себя с параметром end_cond
   ' но в этом примере мы игнорируем этот параметр
   
   ' Мы должны вернуть 1 если цикл ещё не завершён
   ' и 0 когда цикл завершён
   
   ' Тут немного изменённое условие
   ' Но можно взять любое другое условие
   If i < (SizeOf(clause_terminations) \ SizeOf(WString)) AndAlso clause_terminations [i]<> termination Then
      Return 1
   End If
   Return 0
End Operator[/i]
Этот простой пример демонстрирует как добавить сколько угодно условий в оператор For. И без всякой возни с макросами. И будет работать с Continue и вложенными циклами.

На офсайте лежат примеры с переопределением For, рекомендую ознакомиться
https://www.freebasic.net/wiki/KeyPgOpStep

Добавлено (04.09.2023, 23:17)
---------------------------------------------


Цитата
Вообще давно пора инлайн функции, вот где сила.

Это уже давно существует. Компилятору нужно только подсказку сделать:
1. Определяем функцию как Private.
2. Компилируем через gcc "-gen gcc" с оптимизацией (-O 2 или -O 3).

Все приватные функции оптимизатор отлично встраивает.
 
electrikДата: Воскресенье, 17.09.2023, 03:54 | Сообщение # 8
Полковник
Группа: Друзья
Сообщений: 182
Репутация: 3
Статус: Offline
Это всё не плохо конечно, и про перегрузку операторов я знаю, но это получается подгонка оператора под конкретный случай. Я одной строкой не могу написать элегантно. а я говорю про универсальность для базовых типов. В наше время на это можно забить, но в случае  для счётчика базового типа выделять целый класс - ну такое. Ведь внутри все for step next в классе выглядят как отдельные функции. Тоесть, для того чтобы заинкрементировать integer, компилятору нужно вызывать целую функцию. Для настоящего ООП, перегрузка операторов оправдано, но не для базовых типов.
Конечно можно попробовать проделать ваш трюк с приватными функциями и с оптимизацией для GCC, тогда может код развернётся как инлайн. И то я не уверен, что это сработает в классе - нужно проверить.
Нехватает в бейсике гибкого for, ну как не крути.
в C ты можешь даже одним фор инкрементировать несколько переменных, и проверять несколько условий и всё это одной строкой и без перегрузки и без создания своего типа для базового типа.
И я как раз начал этот разговор именно и-за оптимизации, чтобы for был не просто топорный:
for i = 1 to 10
А
for i = 1 to < 10
или
for i = 1 to <=10
или
for i = 0 to i < 10
Всё можно заменить и while и do ... loop, но просто есть более лёгкий путь, и самое главное что там есть что и куда оптимизировать. Таким образом будет меньше ошибок и код оптимизирован и выглядеть элегантно.
В шестедисятые года это придумывалось для базового обучения программированию, но в двадцать первом веке, бейсик вырос, и конструкции тоже должны меняться.
Это перегрузка языка, согласен, но если вы хотите оставить обратную совместимость, этого не избежать.
С удовольствием бы подключился к проекту и сделал то что нужно, но мозгов не хватает разобраться с компилятором. Много было попыток, но безуспешно.
Плохо я знаю теорию построения компиляторов.
 
zamabuvaraeuДата: Среда, 18.10.2023, 19:37 | Сообщение # 9
Подполковник
Группа: Друзья
Сообщений: 149
Репутация: 5
Статус: Offline

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

Например, нам нужна простая программа, которая перечисляет каталоги.
Main.bas:

Код
#include once "FileIterator.bi"
#include once "crt.bi"

Const FormatString = !"%s\r\n"

Function EntryPoint Alias "EntryPoint"() As Integer
   
   For fff As FileIteratorW = WStr("*") To WStr("")
      wprintf(@WStr(FormatString), @fff.FindData.cFileName)
   Next
   
   Return 0
   
End Function

Заголовочный файл FileIterator.bi: 

Код
#ifndef FILEITERATOR_BI
#define FILEITERATOR_BI

#include once "windows.bi"

Type FileIteratorW
   Declare Constructor(ByRef ListingDir As WString)
   Declare Destructor()
   Declare Operator For()
   Declare Operator Step()
   Declare Operator Next(ByRef endCond As FileIteratorW) As Integer
   
   FindData As WIN32_FIND_DATAW
   hFind As HANDLE
   pListingDir As WString Ptr
   resFindNext As BOOL
End Type

#endif
И собственно файл итератора FileIterator.bas:

Код
#include once "FileIterator.bi"

Constructor FileIteratorW(ByRef ListingDir As WString)
   hFind = INVALID_HANDLE_VALUE
   pListingDir = @ListingDir
   resFindNext = TRUE
End Constructor

Destructor FileIteratorW()
   If hFind <> INVALID_HANDLE_VALUE Then
      FindClose(hFind)
   End If
End Destructor

Operator FileIteratorW.For()
   hFind = FindFirstFileW( _
      pListingDir, _
      @FindData _
   )
End Operator

Operator FileIteratorW.Step()
   resFindNext = FindNextFileW( _
      hFind, _
      @FindData _
   )
End Operator

Operator FileIteratorW.Next(ByRef endCond As FileIteratorW) As Integer
   If hFind = INVALID_HANDLE_VALUE Then
      Return 0
   End If
   If resFindNext = 0 Then
      Return 0
   End If
   Return 1
End Operator

Берём шланг и компилируем программу, получаем на выходе исполняемый файл размером 2048 байт (!).
В результирующем коде нет никаких вызовов функций и операторов.

Рядом лежит аналогичный пример без оптимизаций: его размер 23 килобайта.
Прикрепления: FileIterator.zip (24.7 Kb)
 
  • Страница 1 из 1
  • 1
Поиск: