Форум » [x]Harbour » Как смоделировать команду GOTO в Clipper/Harbour » Ответить

Как смоделировать команду GOTO в Clipper/Harbour

Мирослав: В Интернете я случайно наткнулся на несколько старых компьютерных журналов 1986 года, из которых когда-то учился программированию . Там я нашел листинги понравившихся мне, на языке BASIC (численные методы решения систем уравнений). Я легко портировал несколько программ с Basic на Harbour и они работают хорошо и корректно, но с одной я столкнулся с трудностями. Это листинг программы для решения нелинейных уравнений, и проблема в том, что команда перехода GOTO используется во многих местах этой программы. В некоторых местах GOTO используется безоговорочно, а где-то внутри оператора IF...THEN. Поскольку в Clipper/Harbour нет команды GOTO, есть ли возможность смоделировать ее (команду GOTO)? Я пытался что-то сделать, изменить программу, но не смог...

Ответов - 18

MIKHAIL: Оператор goto имеется в C++, вероятно с какими то ограничениями можно вставить в код программы: #pragma BEGINDUMP код на С++ ... #pragma ENDDUMP

PSP: А можете листинг выложить? Аж интересно стало)

alkresin: Тут надо программу смотреть, ее логику - решения возможны разные. Например, оформить кусак кода, куда ведет goto, как фунцию/процедуру и вызывать ее всюду, где надо.


Мирослав: Я отправляю два файла. Один файл представляет собой отсканированную страницу компьютерного журнала который содержит листинг программы. Сканирование довольно плохое (видимо сканеры 1986 года были не такими уж хорошими). Второй файл — это листинг, который я преобразовал в Harbour. Итак, вы видите, что с этим можно сделать. Если вам удастся решить этот «узел Горди» операторов GOTO, это будет супер! Скан объявления из журнала: https://i.postimg.cc/Qd718D1N/Nelinearne-jedna-ine-3.png Мой перевод с Basic на Harbour: ---------------------------------------------------------------------------------------------------------------------- #include "Hwgui.ch" #include "guilib.ch" #include "hbclass.ch" #include "Common.CH" #include "Fileio.CH" #include "Directry.CH" /* Эта часть программы решает нелинейные уравнения вида: (a+bi)X^n + (a+bi)X^n-1 + ... + (a+bi)X^0 = 0, где а – действительная часть коэффициента, b – мнимая часть и n – степень многочлена. FUNC ResavanjePol( aMt, nn ): aMt — матрица размером 2 x n, содержащая действительную и мнимую части коэффициентов nn - степень многочлена Например: 2X^3 -3X^2 + 4X - 1 = 0 nn := 3 aMt:={ ; { 2, -3, 4, -1 }, ; { 0, 0, 0, 0 } ; } */ STATIC a:={}, b:={}, p:={}, q:={}, re:={}, im:={} STATIC i, j, k, e, k1, ii, pp, qq, p1, q1, aa, a1, bb, b1, w, s, z, u, v, n, f, m, l //------------------------------------------------------------------------------------------ FUNC ResavanjePol( aMt, nn ) // Вычисляет все корни многочлена LOCAL aResenje:={}, nZbirImg:=0 // Inicijalizacija() -->Точность расчета взята из файла конфигурации - nTacnost:=0.0000001, поэтому функция Inicijalizacija() не нужна i:=0 j:=0 k:=0 e:=0 k1:=0 ii:=1 pp:=0 // p --> pp В исходной программе переменная "p", я поменял ее на "pp", потому что там тоже есть матрица с таким именем qq:=0 // q --> qq -II- -II- -II- p1:=0 q1:=0 a1:=0 aa:=0 // a --> aa -II- -II- -II- b1:=0 bb:=0 // b --> bb -II- -II- -II- w:=0 s:=0 z:=0 u:=0 v:=0 f:=0 m:=0 l:=0 n:=nn // Степень многочлена e:=nTacnost // Точность расчета a:=ARRAY( n +1 ) b:=ARRAY( n +1 ) p:=ARRAY( n +1 ) q:=ARRAY( n +1 ) re:=ARRAY( n +1 ) im:=ARRAY( n +1 ) FOR i:=1 TO n+1 a:=aMt[1, i] // Действительная (реальная) часть коэффициента p:=a b:=aMt[2, i] // Мнимая (имагинарная) часть коэффициента q:=b nZbirImg += ABS( b ) // Если сумма мнимых (имагинарных) частей коэффициента равна нулю, решения являются действительными числами NEXT IF nZbirImg==0 aResenje:=ARRAY( n ) // Решения — действительные (реальные) числа ELSE aResenje:=ARRAY( 2, n ) // Решения представляют собой комплексные числа END IF // Вычисление корней многочленов модифицированным методом Ньютона: k:=n k1:=k + 1 ii:=1 // GoTo 200 pp:=0 qq:=0 // значение многочлена a1:=p[1] // GoTo 220 b1:=q[1] ++s IF s==100 s:=0 e:=e*10 END IF IF e >= 0.01 RETURN aResenje // Сходимость (конвергенция) не достигнута, конец расчета! Пустая матрица возвращается в основную программу. END IF FOR i:=2 TO k1 w:=pp*a1 - qq*b1 + p // Ряд 260 b1:=pp*b1 + qq*a1 + q a1:=w NEXT w:=a1*a1 + b1*b1 IF ii != 1 GoTo_340() // Переход/прыжок END IF aa:=a1 bb:=b1 j:=1 ii:=2 GoSub850() GoTo_220() // Ряд 330 - Переход - Безусловный прыжок IF ii != 3 // GoTo 340 GoTo_400() // Переход/прыжок END IF IF w < aa*aa + bb*bb GoTo_380() // Переход/прыжок END IF z:=z/2 pp:=p1 + z*u qq:=q1 + z*v // Ряд 360 GoTo_220() // Ряд 370 - Переход/прыжок - Безусловный прыжок aa:=a1 // GoTo 380 bb:=b1 j:=1 ii:=2 GoSub850() // Ряд 390 GoTo_220() // Ряд 390 - Переход/прыжок IF w^j > e*e // GoTo 400 GoTo_430 // Переход/прыжок END IF j:=j + 1 GoSub850() // Ряд 420 GoTo_220() // Ряд 420 - Переход/прыжок IF aa + bb < e*e // GoTo 430 GoTo_610() // Переход/прыжок END IF u:=-(aa*a1 + bb*b1)/w v:=(aa*b1 - bb*a1)/w w:=SQRT(u*u + v*v) f:=v/w f:=ATAN(f)/j IF u < 0 f:=PI()/j - f // Ряд 480 END IF w:=w^(1/j) u:=w*COS(f) v:=w*SIN(f) k1:=k + 1 FOR i:=1 TO k1 p:=a q:=b NEXT ii:=3 z:=1 p1:=pp q1:=qq pp:=p1 + z*u qq:=q1 + z*v GoTo_220() // Ряд 590 - Переход/прыжок // Схема Горнера FOR l:=1 TO j // GoTo 610 ++m re[m]:=p IF ABS(pp) < e re[m]:=0 END IF im[m]:=qq IF ABS(qq) < e im[m]:=0 END IF k1:=k - l + 1 p[1]:=a[1] q[1]:=b[1] FOR i:=2 TO k1 // Ряд 670 p:=a + pp*p[i - 1] - qq*q[i - 1] q:=b + qq*p[i - 1] + pp*q[i - 1] a:=p b:=q NEXT NEXT // Проверка конца // Ряд 730 IF k > j k:=k - j GoTo_200() // Переход/прыжок END IF // Печать результатов: /* Действительные корни находятся в re[], а мнимые корни находятся в im[]. Точность в f. */ RETURN aResenje //------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------- FUNC GoSub850() // Коэффициенты деривации k1:=k - j + 1 FOR i:= 1 TO k1 w:=k1 - i + 1 p:=w*p q:=w*q NEXT RETURN NIL //-------------------------------------------------------------------------------------------

Мирослав: Я тоже пробовал, как вы сказали, но это слишком сложно. Например, оператор GOTO ведет к месту в программе, где есть несколько других операторов GOTO, поэтому программа имеет множественное ветвление. Я пытался, но у меня ничего не получилось, потому что я заблудился, как говорят в Сербии, «как гусь в тумане»...

PSP: Нарисуйте блок-схему. Визуально всё увидите.

Pasha: Вспоминаю 40 лет назад, фортран. Там был goto В общем случае вопрос решения не имеет. Нет в клиппере-харборе такого оператора. Ну нет и все А в частном случае надо разбирать логику программы, как сказал Александр. Выделить управляющие операторы, их совсем немного. Это if-elesif-endif, for-next, case если есть. В тексте вроде бы только видно goto 220. Добавить существующий оператор языка, который бы эмулировал этот goto

SergKis: Мирослав Можете использовать Hash, т.е. ваш код - это наборы блоков кода, goto переход на стоку-позицию в hash, можно добавить в таком случае вариант меток, типа ":met1" и делать переход еще и по меткам (типа Eval или AEval по метке). Что то похожее (типа свой язык со своим препроцессором) делал на clipper, потом перенес в VO на VOScript но оч. давно, в основе был массив PS Нашел, что переносил в hb 2.0 из VO, может пригодится (развить команды, препоцессор можно) [pre2] FUNCTION A2S( aM ) // Массив в форматированную строку LOCAL i,j,k,l,n,t k := Len( aM ) j := "A"+str( k,5 ) FOR i := 1 TO k IF ( t := ValType( aM[ i ] ) ) == "A" j += A2S( aM[ i ] ) ELSEIF t == "C" l := Len( aM[ i ] ) j += "C"+str( l,5 )+aM[ i ] ELSEIF t == "D" j += "D"+DToS( aM[ i ] ) ELSEIF t == "L" j += "L"+iif( aM[ i ],"T","F" ) ELSEIF t == "N" n := LTrim(Str( aM[ i ] )) j += "N"+str( Len( n ),2 )+n ELSE j += "U" ENDIF NEXT RETURN j FUNCTION S2A( cScript, iPointer ) LOCAL iArrayLength LOCAL iItemLength LOCAL iCounter LOCAL cType,cText LOCAL aScript IF iPointer == NIL iPointer := 2 ENDIF iArrayLength := Val( SubStr( cScript , iPointer , 5 ) ) iPointer += 5 aScript := Array( iArrayLength ) FOR iCounter := 1 TO iArrayLength cType := SubStr( cScript , iPointer , 1 ) IF cType == "A" iPointer += 1 aScript[ iCounter ] := S2A( cScript , @iPointer ) ELSEIF cType == "C" iItemLength := Val( SubStr( cScript , iPointer + 1 , 5 ) ) cText := SubStr( cScript , iPointer + 6 , iItemLength ) IF ( SubStr(cText,1,2) == "{|" .OR. SubStr(cText,1,3) == "{ |" ) .AND. ; Right(AllTrim(cText),1)== "}" aScript[ iCounter ] := S2CB(cText) ELSE aScript[ iCounter ] := cText ENDIF iPointer += 6 + iItemLength ELSEIF cType == "D" aScript[ iCounter ] := SToD( SubStr( cScript , iPointer + 1 , 8 ) ) iPointer += 9 ELSEIF cType == "L" aScript[ iCounter ] := ( SubStr( cScript , iPointer + 1 , 1 ) == "T" ) iPointer += 2 ELSEIF cType == "N" iItemLength := Val( SubStr( cScript , iPointer + 1 , 2 ) ) aScript[ iCounter ] := Val( SubStr( cScript , iPointer + 3 , iItemLength ) ) iPointer += 3 + iItemLength ELSEIF cType == "U" aScript[ iCounter ] := NIL iPointer += 1 ENDIF NEXT RETURN aScript FUNCTION S2CB( uCode ) LOCAL cBlockCode,cCode,bBlock,bLastBlock,oErr bLastBlock := ErrorBlock( { | x | oErr := x , _Break( x ) } ) IF valtype(uCode) == "C"; cCode := AllTrim( uCode ) ENDIF IF Empty(cCode) cBlockCode := "{ || NIL }" ELSEIF "{" $ cCode .and. "|" $ cCode .and. "}" $ cCode cBlockCode := cCode ELSE cBlockCode := "{ ||" + cCode + " }" ENDIF BEGIN SEQUENCE bBlock := &( cBlockCode ) END SEQUENCE ErrorBlock( bLastBlock ) IF ! oErr == NIL bBlock := &( "{ || NIL }" ) ENDIF RETURN bBlock FUNCTION _Break( oErr ) BREAK oErr RETURN NIL [/pre2] PS2 Можно делать не препроцессор, а интепретатор команд

Мирослав: Когда я задавал этот вопрос на форуме, я надеялся, что в Clipper/Harbor может быть какая-то функция/команда, которая делает то же самое или похожее на GOTO, о которой я не знал. Поскольку я вижу, что такого не существует, мне придется потрудиться и изменить программу. Скорее всего, я последую совету PSP и нарисую блок-схему (алгоритм). Потом попробую поменять программу, о чем сообщу на форуме.

Мирослав: После долгих раздумий я решил последовать совету, данному мне MIKHAIL-ом, а именно написать эту часть программы на C: #pragma BEGINDUMP код в С (gcc.exe) ... #pragma ENDDUMP В самом начале я столкнулся с проблемой, поэтому хотел бы попросить кого-нибудь, кто в этом разбирается, помочь мне. Вот проблема: Мне нужно вызвать функцию, которая написана в C, из основной программы, которая написана в Harbour, и передать ей три параметра, а именно: 1-двумерная матрица/area (разм. m x n), 2-параметр n и 3-параметр nTacnost. Далее, мне нужно, чтобы эта функция в C возвращала двумерную матрицу решений. Вот пример: FUNC MyProg() LOCAL aResenja:={}... ... ...( Код у Harbour)... ... aResenja:= ResavanjePol( aMatr, n, nTacnost) ... RETURN NIL ... ... #pragma BEGINDUMP ... (код у С - gcc.exe): HB_FUNC( RESAVANJEPOL ) { int n; // Степень полинома, целое число float e; // Точность, 0.0000001 (на пример) float **aMt; // Area dim m x n float **aResenja; // Area dim m x n n=hb_parni(2); e=hb_parnd(3); aMt = hb_par??? ; // Я не знаю как правильно написать эту часть кода aResenja = (???)hb_xgrab( ??? ); // Я не знаю как правильно написать эту часть кода ... ... ... ??? hb_ret???( aResenja ); // Я не знаю как правильно написать эту часть кода } #pragma ENDDUMP Части кода, окрашенные в красный цвет, я не знаю, как правильно написать, поэтому мне нужен кто-то, кто умеет их писать. (Если я где-то в коде допустил ошибку, мне бы тоже хотелось, чтобы меня поправили.)

MIKHAIL: давненько не писал, могу ошибиться, надеюсь коллеги поправят меня: aMt = hb_param( 1, HB_IT_ARRAY ); hb_itemRelease( hb_itemReturn( aResenja ) );

Мирослав: То, что вы написали, работает хорошо (aMt = hb_param( 1, HB_IT_ARRAY ); и hb_itemRelease( hb_itemReturn( aResenja ) ); ). Как мне получить доступ к элементам двумерной матрицы (area), aMt[m][n] ? Когда я пишу aMt[0][0] и aMt[0][1], программа зависает... И как мне зарезервировать меморию для матрицы aResenja ?

alkresin: Передача и возврат массивов Harbour/C, особенно многомерных, - непростое дело Прежде всего, надо получить харборовский массив: PHB_ITEM pArray = hb_param( 1, HB_IT_ARRAY ) Его формат хранения, тип данных - это не C массив, к нему нельзя обращаться как к C массиву, с помощью квадратных скобок. Его можно пройти от начала до конца, из каждого элемента извлечь вложенный массив, этот вложенный массив тоже пройти от начала до конца и извлечь каждый его элемент - это уже будет число из вашей матрицы: [pre] PHB_ITEM pArray = hb_param( 1, HB_IT_ARRAY ), pSubArr; ULONG ul1, ulLen1 = hb_arrayLen( pArray ), ul2, ulLen2; float myfloatNum; for( ul1 = 1; ul1 <= ulLen1; ul1++ ) { // вот так мы извлекаем вложенный массив: pSubArr = hb_arrayGetItemPtr( pArray, ul ); ulLen2 = hb_arrayLen( pSubArr ); for( ul2 = 1; ul2 <= ulLen2; ul2++ ) { // Вот так мы извлекаем float число: myfloatNum = (float) hb_arrayGetND( pSubArr, ul2 ); // Далее надо вставить эту myfloatNum в ваш aMt ... } } [/pre] Если размерность aMt переменная, то для него надо предварительно зарезервировать место с помощью hb_xgrab примерно так: aMt = (float**) hb_xgrab( sizeof(float) * m * n ); Если m и n - константы, то проще: float aMt[m][n]; Ну а с возвратом ( aResenja) - похожая операция. Создается массив: PHB_ITEM aMetr = hb_itemArrayNew( m ); Потом для каждого элемента создается вложенный масиив и уже в каждый его элемент засовывается число. Смотрите примеры в текстах Harbour, HwGUI

alkresin: А можно, наверное, и не создавать aMt и не заполнять его из pArray в цикле, а просто везде в программе вместо aMt[x][y] писать: (float) hb_arrayGetND( hb_arrayGetItemPtr( pArray, x+1 ), y+1 );

Pasha: Обращение к элементам массива aMatr[i, j] (aMt): PHB_ITEM pArray = hb_param( 1, HB_IT_ARRAY ); PHB_ITEM pSub = hb_arrayGetItemPtr( pArray, i ); // подмассив HB_LONG ll = hb_arrayGetNL( pSub, j ); или (зависит от типа данных) double dd = hb_arrayGetND( pSub, j ); элементы массива нумеруются с единицы Создание массива m*n: [pre] PHB_ITEM pRet = hb_itemArrayNew( m ), pSubArray; for( i = 1; i <= m; i ++) { pSubArray = hb_itemArrayNew( n ); for( j = 1; j <= n; j ++) { hb_arraySetNI( pSubArray, j, ll ); или hb_arraySetND( pSubArray, j, dd ); } hb_arraySet( pArray, i, pSubArray ); hb_itemRelease( pSubArray ); } [/pre] Возврат массива hb_itemReturnRelease( pRet );

Мирослав: Благодаря очень подробному объяснению Александра и Паши, подкрепленному соответствующими примерами, я понял, как работает передача и возврат многомерных матриц Hrb/C! Теперь я могу продолжать работать. Большое спасибо всем, кто помог мне решить эту проблему! P.S. «Array» на сербском языке называется «матрица». Как правильно сказать по-русски - "матрица" или "массив" ?

alkresin: Матрица (двумерный массив) употребляется как математический термин. А термин "массив" больше употребляется в программировании и носит более общий характер, он ведь может иметь произвольное число измерений

Мирослав: Мне удалось перевести на C часть программы, написанную на BASIC, содержащую большое количество операторов GOTO, и она работает хорошо и корректно. Но я столкнулся с новой проблемой: я не могу получать фокус там, где хочу. Я использую функцию hwg_SetFocus(), но она работает не так, как я бы хотел. Вот в чем дело - когда я запускаю часть своей программы, содержащую окно, то внутри окна BROWSE с двумерным массивом и под таблицей (внутри окна) три клавиши/кнопки, фокус всегда на первой клавише , и я бы хотел, чтобы оно было в BROWSE. Итак, я попробовал следующее: 1) INIT DIALOG oDlg TITLE "Унос коефицијената система једначина " + Any2Str(n) + " x " + Any2Str(n) ; AT 0, 0 SIZE nSirinaP, nVisinaP NOEXIT ICON oIcon ; ON INIT { || oBtn2:Enabled:=.F., hwg_SetFocus(oBrwArr:handle ) } ; STYLE WS_DLGFRAME + WS_SYSMENU + DS_CENTER @ 0, 0 BROWSE oBrwArr ARRAY ; STYLE WS_VSCROLL + WS_HSCROLL ; SIZE nSirinaT, nVisinaT .... .... .... @ 10, nVisinaP-70 BUTTON oBtn1 CAPTION "Реши систем" SIZE 100, 32 ON CLICK { || ResiSistem( oBrwArr, n ), oBtn2:Enabled:=.T. } FONT oFontBtn ; TOOLTIP "Решавање система од " + ; Any2Str(n) + IIF( n < 5, " једначине ", " једначина " ) + "са " + Any2Str(n) + IIF( n < 5, " непознате.", " непознатих." ) @ 120, nVisinaP-70 BUTTON oBtn2 CAPTION "Штампај" SIZE 100, 32 ON CLICK { || Stampa( oBrwArr, n ) } FONT oFontBtn ; TOOLTIP "Штампање резултата прорачуна." @ nSirinaP-110, nVisinaP-70 BUTTON oBtn3 CAPTION "Излаз" SIZE 100, 32 ON CLICK { || oDlg:Close() } FONT oFontBtn ; TOOLTIP "Затварање табеле и повратак у Главни мени." oDlg:Activate() (Фокус находится на клавише oBtn1.) 2) ..... ..... ..... hwg_SetFocus(oBrwArr:handle ) oDlg:Activate() (Фокус все еще находится на клавише oBtn1.) 3) ..... ..... ..... ACTIVATE DIALOG oDlg ON ACTIVATE { || hwg_SetFocus( oBrwArr:handle ) } (Фокус все еще находится на клавише oBtn1.) А потом я увидел, что не знаю, как получать фокус на oBrwArr, поэтому решил обратиться за помощью...



полная версия страницы