forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   Полезные функции (http://forum.boolean.name/forumdisplay.php?f=17)
-   -   Возвращение нескольких результатов из функции + Разбиение строки по символу (http://forum.boolean.name/showthread.php?t=17167)

Жека 10.08.2012 14:36

Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Метод №1: использование глобальных массивов.

У этого метода есть ограничение, о котором ниже, но для простых каких-то задач сгодится.
Как альтернатива - свои списки (упрощённые) использовать (может допишу с ними пример сделал, ниже по теме "метод №2").

Была задачка разбить строку на подстроки по знаку-разделителю. Я захотел вернуть массив строк.
Просто через "Return mas" блитц не умеет. Поэтому я сделал глобальный массив для хранения результатов работы функции.

Код:

;инфа для возврата значений из функций
Dim RETVAL_STR$(0)
Global RETVAL_COUNT%

;smb - 1 символ типа запятой
Function fnSplitString(s$,smb$=",")
        Local size% = Len(s)
        Local cnt% = 0, k%
        ;сначала находим количество символов-разделителей
        For k% = 1 To size
                If(Mid(s,k,1) = smb) Then cnt = cnt + 1
        Next
        RETVAL_COUNT = cnt+1 ;запоминаем размер
        Dim RETVAL_STR(cnt) ;расширяем до нужного размера
        If(cnt = 0)
                RETVAL_STR(0) = s
                Return
        EndIf
        ;теперь снова проходим, заполняя глобальный массив
        Local val$ = "", char$, ind% = 0
        For k% = 1 To size
                char = Mid(s,k,1)
                If(char = smb)
                        RETVAL_STR(ind) = val
                        val = ""
                        ind = ind+1
                Else
                        val = val + char
                        If(k = size) Then RETVAL_STR(ind) = val ;последнюю подстроку тоже
                EndIf               
        Next
End Function

На оптимальность работы функции я не претендую, мне нужно от неё парсить параметры в строчках типа
Код:

130,30,30
в момент загрузки уровня.

Пример использования:
Код:

fnSplitString("100,exit,-7")
For k% = 0 To RETVAL_COUNT-1
        DebugLog("RETVAL_STR("+k+") = "+RETVAL_STR(k))
Next

Теперь о грустном. Вот это работать будет криво или загнётся:
Код:

Local s1$ = "0,0;1,0;1,1;0,1", s2$
fnSplitString(s1, ";") ;сначала разбиваем по точке-с-запятой
For k% = 0 To RETVAL_COUNT-1
        DebugLog("RETVAL_STR("+k+") = "+RETVAL_STR(k))
        fnSplitString(RETVAL_STR(k),",") ;теперь по запятой
        For i% = 0 To RETVAL_COUNT-1
                DebugLog("RETVAL_STR_2("+i+") = "+RETVAL_STR(i))
        Next
Next

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

Hartmann1 10.08.2012 15:09

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Цитата:

Сообщение от Жека (Сообщение 235901)
Код:

        Dim RETVAL_STR(cnt) ;расширяем до нужного размера
        If(cnt = 0)
                Dim RETVAL_STR(0)
                RETVAL_STR(0) = s
                Return
        EndIf


Зачем еще раз "Dim RETVAL_STR(0)" делаешь, если cnt и так будет 0? И Dim с нулем удаляет массив, нужно Dim 1 делать.

Цитата:

Сообщение от Жека (Сообщение 235901)
Код:

If(k = size) Then RETVAL_STR(ind) = val ;последнюю подстроку тоже

Это вообще вынести из цикла, причем без условия "If(k = size)"

Переменная k не определена как Local.

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

Ну а насчет затирания предыдущих результатов, если не лень, можно написать для каждого типа парсинга свой вариант :)

ЗЫ
По коду еще, мне кажется для длинных строк будет эффективнее сделать авторазмер массива ( как например в С++ std::vector, каждый раз в два раза ), нежели сначала делать предрасчет.
Правка: забыл что в блице нет ReDim и новый Dim удаляет старые данные.

Ну и в крайнем случае можно банки использовать, неудобно конечно что встроеного PokeString нету.

Жека 10.08.2012 18:26

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Цитата:

Сообщение от Hartmann1 (Сообщение 235905)
Зачем еще раз "Dim RETVAL_STR(0)" делаешь, если cnt и так будет 0? И Dim с нулем удаляет массив, нужно Dim 1 делать.

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

Это вообще вынести из цикла, причем без условия "If(k = size)"
Можно.
Цитата:

Переменная k не определена как Local.
Некритично.
Цитата:

Как вариант можно передавать через параметры функции статические массивы, просто заранее определить максимальный размер, некрасиво конечно, но как вариант.
Вот про это подробнее, пожалуйста, если это не из серии забыл что речь о блитце.

Спасибо за комменты :)

Hartmann1 10.08.2012 19:30

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Цитата:

Сообщение от Жека (Сообщение 235934)
Нифига не удаляет, я проверял.

Как ты проверял?:)
Цитата:

Сообщение от Жека (Сообщение 235934)
Некритично.

Тем не менее твой код может заюзать какой-нить невнимательный нуб, у которого окажется глобальная k (и не такие бывают же :)
Цитата:

Сообщение от Жека (Сообщение 235934)
Вот про это подробнее, пожалуйста, если это не из серии забыл что речь о блитце.

А чего подробнее, через параметры можно передавать массивы с квадратными скобками, статические которые. В твоем случае будет
Код:

fnSplitString(s$,smb$=",",strings$[константный размер])
Только вот я не подумал что передавать-то туда нужно такого же размера массив :) Короче лучше банки юзать.
А еще лучше хранить данные в бинарном виде :)

Жека 11.08.2012 07:48

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Цитата:

Сообщение от Hartmann1 (Сообщение 235948)
Как ты проверял?:)

Код:

Dim a$(0)
a(0) = "hi world!"
DebugLog(a(0))

пашет отличненько-с (я сам удивился, но раз оно так, то что поделать?)

Глобальная К - опасно, сам такое не сделаю, но всё же полезно при написании кода на экспорт людям.

"Массивы с квадратными скобками" - для данного примера не сгодятся, но на будущее припомню.

Hartmann1 11.08.2012 13:04

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Цитата:

Сообщение от Жека (Сообщение 235992)
Код:

Dim a$(0)
a(0) = "hi world!"
DebugLog(a(0))

пашет отличненько-с (я сам удивился, но раз оно так, то что поделать?)

Глянул справку и точно ведь, я забыл что в блице выделяется на 1 элемент больше чем указано :)
Кстати -1 тоже корректный размер, и судя по тому что доступ к нулевому элементу вызывает "index out of bounds", память освобождается.

Жека 13.08.2012 08:47

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Метод №2: использование связных списков

Здесь пример использования подобия связного списка для возвращения неограниченного числа параметров из функции.

Код:

;создание списка с параметрами - головной узел
Function fnRetValCreate.TRetVal(s$="", i%=0, f#=0)
        Local rv.TRetVal = New TRetVal
        rv\root = rv
        rv\sVal = s
        rv\iVal = i
        rv\fVal = f
        Return rv
End Function

;добавление нового результата
Function fnRetValAdd.TRetVal(retval.TRetVal, s$="", i%=0, f#=0)
        ;если ещё не создан, то создаём новый
        If(retval = Null) Then Return fnRetValCreate(s, i, f)
        retval\nxt = New TRetVal
        retval\nxt\root = retval\root
        retval\nxt\sVal = s
        retval\nxt\iVal = i
        retval\nxt\fVal = f
        Return retval\nxt
End Function

;удаление всех результатов
Function fnRetValDelete(retval.TRetVal)
        Local node.TRetVal = retval\root
        While(node <> Null)
                retval = node
                node = node\nxt
                Delete(retval)
        Wend
End Function

;тип для хранения результатов (строки, целые и дробные)
Type TRetVal
        Field sVal$, iVal%, fVal# ;сами данные
        Field nxt.TRetVal ;указатель на следующего
        Field root.TRetVal ;указатель на головной узел
End Type

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

Код:

Local cl$ = CommandLine()
If(cl = "") Then End
DebugLog(cl)

Local rv.TRetVal = fnParseCommandLine(cl)
Local node.TRetVal = rv
Local file% = WriteFile("files.txt")
;пробегаем по всем результатам
While(node <> Null)
        WriteLine(file, node\sVal)
        node = node\nxt
Wend
CloseFile(file)
;удаляем список с результатами
fnRetValDelete(rv)

End

;в командной строке параметры, содержащие пробелы, обрамляются кавычками
;остальные - нет
Function fnParseCommandLine.TRetVal(cl$)
        Local size% = Len(cl)
        Local k%, cnt%, s$="", char$, q$ = Chr(34) ;кавычка
        Local retval.TRetVal
        For k% = 1 To size ;проходим по всем буквам
                char = Mid(cl, k, 1)
                If(char = q) ;встретилась кавычка
                        cnt = cnt+1
                        DebugLog("1")
                        If(cnt = 2) ;закрылась кавычка
                                retval = fnRetValAdd(retval, s)
                                s = ""
                                k = k+1 ;пропускаем пробел
                                cnt = 0
                        EndIf
                Else If(char = " " And cnt = 0) ;если пробел не внутри кавычек, то он отделяет параметры
                        retval = fnRetValAdd(retval, s)
                        s = ""
                        DebugLog("2")
                Else
                        s = s + char
                        If(k = size) ;если достигли конца строки, то последний параметр забираем тоже
                                DebugLog("3")
                                retval = fnRetValAdd(retval, s)
                        EndIf
                EndIf
        Next
        Return retval\root ;возвращаем указатель на корень списка
End Function

ПС. Если кто-то знает как в блитц максе выцепить из командной строки русские символы, то подскажите - нужно такое: кидаем файл, содержащий в пути или названии кириллицу, на ехе-шник, и нужно достать/преобразовать путь так, чтоб можно было сделать с ним LoadImage() и прочее.

Черный крыс 10.11.2012 05:36

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Эээээ так это ты так с БМаксом изголяешься??? 0_о

На БМаксе так делать это всеравно что с куклой на свидание сходить =)

В БМаксе функции могут возвращать что угодно, хоть массивы, хоть контейнеры.

Ну а если надо указать прямо, то для таких целей там есть специальный оператор - "Var", который передает прямой адрес переменной.

H@NON 10.11.2012 12:11

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
написано же, раздел для Blitz3D...

Жека 30.11.2012 11:47

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Diablo1909, мои примеры выше для Blitz3D.
Научи меня вот этому:
Цитата:

Если кто-то знает как в блитц максе выцепить из командной строки русские символы, то подскажите - нужно такое: кидаем файл, содержащий в пути или названии кириллицу, на ехе-шник, и нужно достать/преобразовать путь так, чтоб можно было сделать с ним LoadImage() и прочее.

Черный крыс 30.11.2012 17:13

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Кирилицу в названиях файла всегда надо убирать. Ибо не везде работает.

Жека 03.12.2012 06:50

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Приходится убирать :) Я для себя утилитки пишу, поэтому универсальность не нужна.

kostya261 28.12.2012 16:29

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Цитата:

Сообщение от Жека (Сообщение 235901)
Метод №1: использование глобальных массивов.
На оптимальность работы функции я не претендую, мне нужно от неё парсить параметры в строчках типа
Код:

130,30,30

Я конечно в этом мало понимаю, но что если юзать либо списки (type), либо банки?

kostya261 28.12.2012 16:33

Ответ: Возвращение нескольких результатов из функции + Разбиение строки по символу
 
Цитата:

Сообщение от Hartmann1 (Сообщение 235905)
Ну и в крайнем случае можно банки использовать, неудобно конечно что встроеного PokeString нету.

Так его написать, две минуты времени нужно :)


Часовой пояс GMT +4, время: 14:07.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot