Todo sobre... LET - variables y funciones de cadena Este texto reune todas mis notas sobre LET y como se combina con otras instrucciones relacionadas con la misma, además de las funciones de cadena existentes en el Basic, así como la definición de aquellas funciones que no hay en el ZX SPECTRUM. Dicho esto, paso a... LET El termino ingles LET, se puede traducir literalmente como "dejar" en castellano. Pero en terminos informaticos se usa mas bien con el sentido de "asignar" y es por ello que se la conoce como sentencia de asignacion de variables. sintaxis: {LET} v{$}= exp Requerido por algunos pocos Basics como el Darmouth o Sinclair, es opcional en la mayoria. Originalmente estuvo limitado al manejo de variables numericas solamente. Algunos Basics como el del Spectrum permiten asignar valores dependiendo de alguna condicion como AND. Ejemplo: LET x=(100 AND m=5)+(200 AND m=2)+(300 AND m=7) READ y DATA vs LET La sentencia LET no es la unica que permite asignar valores a variables. La dupla READ-DATA tambien lo hace. Solo veamos el ejemplo: 10 READ A$,B,C$,D 20 DATA "AZUL",12,"SI",3.5 es equivalente a: 10 LET A$="AZUL":LET B=12 20 LET C$="SI":LET D=3.5 Puede ser quiza menos claro que LET pero es menos repetitivo y al mismo tiempo ahorra espacio en memoria y tiempo de tipeo. Y eso se nota mas, si en vez de cuatro variables tuvieramos que asignar mas de diez, como en el caso de las variables con sub indice como son las matrices DIM. En otros lenguajes El lenguaje LOGO tiene la primitiva MAKE para asignar valores a variables. COMAL tambien tiene LET pero es opcional y en vez del signo '=', usa ':=' como el Pascal y ALGOL. LISP en cambio usa SET, SETQ, CSET Y CSETQ para la creacion de variables y constantes. Algunos FORTH tienen CONSTANT y VARIABLE. CONSTANTES, VARIABLES Y ETIQUETAS En PASCAL es necesario asignar al comienzo del programa mismo que valores seran usados como constantes, cuales como variables y cuales serviran de etiquetas. Ejemplo: label 9991; (*etiqueta salto goto*) const meses = 12; diasem = 7; var contale,contadi,contestp : integer; caract : char; Como se ve en el ejemplo, el PASCAL pone primero LABEL siempre que exista la necesidad de un salto GOTO invitable. Y mientras las constantes ya se le asignan valores desde el comienzo, las variables son definidas por el tipo al cual perteneceran, es decir, numericas o alfanumericas para luego dentro del programa asignarles sus valores respectivos. FORTRAN tambien usa etiquetas para GOTO pero no es necesario inicializarlas al comienzo del programa, y tambien se puede designar que variables seran segun que tipo. En comparacion, la mayoria de BASICs inicializa a cero todas las variables numericas y a nulo todas las cadenas. En el caso del Spectrum, eso no pasa y hay que iniciarlas al comienzo para evitarse el error de "variable no encontrada". Mientras que Atari requiere DIM para inicializar las variables de cadena, y otros micros requieren de CLEAR para eso mismo. TIPOS DE VARIABLES Todos los lenguajes de programacion trabajan con varios tipos de variables. PASCAL por ejemplo, acepta variables de tipo alfanumerico, booleano, numerico (entero, real), escalares, conjuntos y registro. La mayoria de BASICs de los 8bits acepta cadenas ($) y variables numericas enteras (%) o reales (algunos micros como el TRS-80 usan el signo # para identificar reales). El BASICA y GW-Basic para PCs divide los reales en dos: simple precision (!) y doble precision (#). NOMBRES DE VARIABLES Algunas versiones de BASIC aceptan una sola letra (como es el caso de Tiny Basic y sus derivados). Otras aceptan nombres largos (tanto numericas como alfanumericas). Algunos micros solamente aceptan las 2 primeras letras de un nombre con lo que PHASE y PHOTO pasan a ser la misma variable. En el caso del Spectrum este acepta nombres largos con espacios incluso solo para variables numericas, pero las variables alfanumericas y matrices en general o nombres de funciones y bucles FOR,son de una sola letra. Para el Basic sinclair es indiferente si el nombre de la var. esta en mayusculas o minusculas, es decir valor y VALOR son la misma var. del mismo modo que lo hace el PASCAL, a diferencia del C que las considera dos vars. distintas. Ahora, mientras que en el Spectrum todos los caracteres son significativos para el nombre, en los demas micros pasa lo sigte.: el Osborne con Basic Microsoft, acepta nombres de hasta 40 caracteres, siendo el primero siempre una letra. El TRS-80 solo considera significativos los primeros 6 car. de un nombre mas largo. El C-Basic acepta hasta 31 car. Tanto el Apple, Commodore o Dragon 32 solamente admiten validos los primeros dos car. pero su longiud varia. Dragon acepta nombres de hasta 40 car. Apple hasta 100 en su Basic entero y 238 con Applesoft pero el CBM llega a los 256 car. OTRAS SENTENCIAS Y FUNCIONES ASOCIADAS CLEAR sintaxis1: CLEAR {n} Al parecer no existe en los BASICs del Galaxia y UK-101 ni en las primeras versiones de Darmouth. En el Basic BBC, CPC, QL y Apple II carece de parametros y borra las variables dinámicas. En algunos BASICs el parametro n permite reservar n bytes de la memoria para las cadenas. Mientras el Basic del Spectrum usa n para reservar espacio en la RAM mas alla de n para rutinas en codigo maquina a la vez que borra todas las variables, por lo que debe usarse una vez siempre al inicio del programa. sintaxis2: CLEAR[,m{,n}] es la forma existente en versiones del Basic Microsoft como el GW Basic, donde m es usado para rutinas en codigo maquina y n asigna el tamaño de la pila dedicada a sentencias como GOSUB, FOR o incluso PAINT. CLR Esta version de CLeaR existe en los BASICS de Atari, Commodore y el BASIC entero de Apple. Sin parametros, solamente libera el espacio usado por las variables y matrices existentes en la memoria, igual que el CLEAR sin parametros. SWAP Permite intercambiar el valor de unas variable por otra. Inexistente en el Basic Sinclair, Atari, Apple o Commodore. sintaxis: SWAP var1,var2 donde: var1 y var2 son nombres de variables ya sean numéricas o de cadena. En el caso del Spectrum hay que definirla según sea: Subrutina Swap numerica= LET swap=var2 LET var2=var1 LET var1=swap Subrutina Swap alfanumerica= LET s$=b$ LET b$=a$ LET a$=s$ Si se tiene BetaBasic 3 o 4, se puede usar el sigte procedimiento: DEFPROC swap,a$,b$ LOCAL c$ LET c$=b$,b$=a$,a$=c$ ENDPROC Ejemplo: 10 LET x=10, y=100 20 swap "x","y" 30 print x,y DEFINT - define las variables como enteras DEFDBL - define las variables como reales de doble precision DEFSNG - define las variables como de simple precision DEFSTR - define las variables como cadenas FIX - funcion truncada, si x positivo entonces FIX(x)=INT(x) si x negativo entonces FIX(x)=INT(x)+1 CINT - convierte a entero por el redondeo al valor mas proximo CDBL - convierte x simple precision a valor de doble precision CSNG - convierte x doble precision a valor de simple precision CHANGE - cambia una cadena a lista de numeros ASCII y viceversa VARPTR - devuelve la direccion en memoria de una variable ADR - funcion de ATARI que devuelve la direccion en memoria de la variable de cadena usada como argumento. FUNCIONES DE CADENA Ya he tratado en otras notas sobre LEN, CHR$, STR$ y ASC cuyo equivalente en el Spectrum es CODE. Así que esta vez reuno aquí mis notas sobre esas funciones de cadena inexistentes en el Spectrum y el modo de definirlas. LEFT$(v$,x) Esta función lo que hace es coger 'x' caracteres a la izquierda de la cadena v$. Inexistente en el Basic Sinclair. Su equivalente es: v$(TO x). Ejemplos: Microsoft Sinclair LEFT$("MCKOY",3) "MCKOY"(TO 3) LEFT$(A$,22) A$(TO 22) LEFT$(Z$,1) Z$(1) Y si quisieramos definirla: DEF FN L$(v$,x)=v$(TO x) Nota: Viendo los ejemplos, podria decirse que la forma Sinclair es mas breve de tipear que la de Micosoft, y mejor en el caso del ultimo ejemplo. RIGHT$(v$,x) Como LEFT$, pero cogiendo 'x' caracteres a la derecha de la cadena v$. Inexistente en el Basic Sinclair. Su definición: DEF FN R$(v$,x)=v$(LEN v$-x+1 TO) Ejemplo: PRINT RIGHT$("MARIA",3) - y en el Spectrum: PRINT FN R$("MARIA",3) Nota: Si en un programa solo se requiriese una vez, basta con tipear la formula de (LEN...TO...) pero si fuera necesaria usarla mas de una vez en el programa, es mucho mejor definirla en vez de repetir una y otra vez dicha formula. MID$ MID$ puede hacer lo mismo que LEFT$ y RIGHT$. Inexistente en el Basic Sinclair. sintaxis: MID$(v$,x[,y]) toma 'y' caracteres a partir de 'x' de la cadena v$. Si se omite 'y', entonces coge de 'x' hasta el ultimo caracter de v$. A diferencia de LEFT$ y RIGHT$, MID$ puede ser usada en algunos BASICs tambien como sentencia, siendo su segunda sintaxis: MID$(v$,x,y) = var$ COMO FUNCION: Microsoft Sinclair MID$("MORGAN",1,4) "MORGAN"(TO 4) MID$("MORGAN",4) "MORGAN"(4 TO) MID$("MORGAN",3,2) "MORGAN"(3 TO 4) MID$("MORGAN",3,1) "MORGAN"(3) Nota: por los ejemplos podemos ver que la norma Sinclair es mas breve y clara, en lo que al Spectrum se refiere. Pero si aun se la desea definir tenemos... DEF FN M$(v$,x,y)=v$(x TO (x+y-1)) DEF FN M$(v$,x,y)=v$(x TO ((x+y-1)AND y>0)+(LEN v$ AND y<1))) Nota: la primera definicion es suficiente para MID$(v$, x, y), no pudiendo ser ningun parametro opcional. La segunda permite leer hasta el final de la cadena pero con la sigte. diferencia: MID$("MORGAN",4) FN M$("MORGAN",4,0) Como sentencia: Microsoft Sinclair MID$(A$,1,4)="MORGAN" LET A$(TO 4)="MORGAN" Asi que su formula completa como sentencia en el Basic Sinclair es: LET M$(x TO (x+y-1))=v$ Como ya se ha mencionado, la gran mayoria de BASICs solo acepta MID$ como funcion pero no como sentencia. SEG$(v$,x,y) existente en las primeras versiones de BASIC, SEG$ es usada en lugar de las 3 funciones antes mencionadas. Inexistente en el Basic Sinclair y otros 8bits. La diferencia con MID$ es que lee desde 'x' hasta 'y' en vez de leer 'y' caracteres a partir de 'x'. Comparacion: SEG$("MORGAN",3,4) "MORGAN"(3 TO 4) INPUT$ (n) Permite ingresar 'n' caracteres sin ENTER al final. A diferencia de INPUT, esta funcion no hace 'eco' en pantalla nada de lo que se tipea por el teclado. Muy util si no queremos ver lo que se tipea y sabemos de paso la longitud maxima 'n' del texto a ingresar. Inexistente en el Spectrum. De ser requerida se la puede definir del modo sigte.: subrutina FN input$(n)= DIM i$(n) FOR x=1 TO n PAUSE 0: LET i$(x)= INKEY$ NEXT x: RETURN Otra forma exclusiva de definir la subrutina FN input$(n)= LET i$="" FOR x=1 TO n INPUT USR 5598 LET i$= i$+ CHR$ PEEK 23559 NEXT x: RETURN INSTR (n,x$,y$) Busca la cadena y$ dentro de la cadena x$ a partir de n. Permite obtener también la posición de una cadena de caracteres dentro de otra cadena. Inexistente en el Basic Sinclair. Existe en Betabasic bajo el nombre de INSTRING. En el Superbasic de QL, INSTR es un operador de inclusion y su sintaxis es: x$ INSTR y$. Al igual que con INPUT$, se requiere de una subrutina para su definicion: subrutina FN instr(n,x$,y$)= FOR n=1 TO LEN x$ IF x$(n)=y$ THEN RETURN NEXT n: RETURN Para encontrar subcadenas hacer las modificaciones sigtes: LET mm= LEN y$: LET mn= LEN x$ FOR n=1 TO mn IF n+mm >= mn THEN LET n=mn IF x$(n TO n+mm)=y$ THEN RETURN NEXT n: RETURN Ejemplo aplicación de INSTR: 1 GOTO 10 2 REM subr. FN instr$(x)= 3 FOR x=n TO LEN a$ 4 IF a$(x)=b$ THEN RETURN 5 NEXT x: RETURN 10 LET a$="Sr. Mortimer. McKoy." 20 LET n=5: LET b$="." 25 GOSUB 3: LET a=x 30 LET b$= a$(TO a-1) 40 PRINT b$ STRING$(n,m{$}) Repite la cadena m$ (o el valor ASCCI de m), n veces. Inexistente en el Basic Sinclair. Existe en Batabasic. Su equivalente en Superbasic es FILL$(n$,n). Se la puede definir del modo sigte: subrutina FN string$(n,n$)= DIM i$(n) FOR x=1 TO n LET i$(x)=n$ NEXT x: RETURN Otra forma de definir la subrutina FN string$(n,n$)= LET s$="" FOR x=1 TO n LET s$=s$+n$ NEXT x: RETURN Ejemplo aplicación de STRING$: 1 GOTO 100 2 REM subr. FN string$(n,n$)= 3 DIM g$(n) 4 FOR x=1 TO n 5 LET g$(x)=b$ 6 NEXT x: RETURN 100 INPUT "Introduzca nombre ";n$ 110 PRINT n$: LET n=25-LEN n$ 120 LET b$=".": GOSUB 2 125 PRINT g$ 130 INPUT "Si desea continuar, escriba SI ";r$ 140 IF r$="SI" THEN GOTO 100 Nota: la primera definicion es optima si solo se repite un unico caracter, en cambio, la segunda permite repetir mas de un caracter de n$. SPACE$(n) / SPC(n) Funciones que generan una cadena de espacios en blanco de largo 'n'. La diferencia entre ambas funciones es que SPC solo puede ser usada dentro de PRINT y LPRINT, pues no devuelve ninguna cadena a diferencia de SPACE$, la cual si puede ser usada dentro de variables de cadena. Ya que el Spectrum carece de ambas funciones, debemos entonces buscar alternativas. Una forma de definirla directamente es con DIM: DIM s$(255) - 255 es el valor máximo del argumento de SPACE$ para usarlo: PRINT s$(TO n) Un segundo modo es con una FN llena de espacios: DEF FN s$(n)= "(m espacios)"(TO n) - por ejemplo, m= 64 para usarlo: PRINT FN s$(n) Un tercer método es una subrutina con FOR - NEXT: REM subr. FN spc LET s$="" FOR x=1 TO n LET s$=s$+" " NEXT x: RETURN Y para usarlo: LET n=espacios: GOSUB spc: PRINT s$ Ejemplos: 10 PRINT "AAA";FN s$(5);"BBB" 5 DIM s$(255) 10 LET i=1 20 LET l$= s$(TO i) 30 PRINT l$;i 40 LET i=i+1 50 IF i<=8 THEN GOTO 20 Notas: SPC - en BBC Basic el maximo funcional es 255, a partir de 256 se vuelve a 1 en adelante. En GW Basic y Amstrad el maximo es 80, si se pasa vuelve a 1. MSX Bas. devuelve error más allá de 255. Commodore idem pero sin borrar lo existente. Space$ - MSX Basic devuelve error más allá de 200. Thompson, Amstrad y GW-Basic devuelven error más allá de 255. Philips da error mas alla de 50. VARPTR$ - devuelve cadena de 3 bytes conteniendo el tipo de variable y la direccion en memoria de la misma El Spectrum carece de una funcion equivalente a VARPTR o ADR, el unico modo es viendo en las variables del sistema VARS (direccion de las variables 23627-8) y DEST (dir. de las vars. en la asignacion 23629-30): Betabasic tampoco ofrece nada que equivalga pero talvez se pueda buscar una variable mediante MEMORY$(). Otras funciones numericas y de cadena Existen ademas HEX$, OCT$, BIN$ (para conversion de sistemas de numeracion) asi como CVI, CVD, CVS, MKI$, MKD$, MKS$ (todos estos ultimos exclusivamente para manejo de ficheros directos) y DATE$ y TIME$ (para fecha y tiempo). Excepto OCT$ y DATE$, casi todas las funciones antes mencionadas tienen equivalentes en BetaBasic. Hasta el momento la gran mayoria de instrucciones y funciones BASIC que he mencionado aqui son del B. Microsoft, excepto CHANGE, SEG$ o BIN$. El Basic Locomotive posee muchas de las funciones ya dichas y ademas otras dos funciones que son UPPER$ y LOWER$ (para convertir de minusculas a mayusculas y viceversa) que tienen su equivalente en SHIFT$ del Betabasic y como UCASE$ y LCASE$ del QBasic. PARA CERRAR Recuperar variables (Publicado originalmente en Microhobby #30 seccion TRUCOS p.7) La rutina sigte.: 42,75,92 ld hl,(23627) 126 cheq ld a,(hl) 254,193 cp 193 ;(ascii de "A"+128) 40,6 jr z, exit 205,184,25 call 19B8 235 ex de,hl 24,245 jr cheq 54,65 exit ld (hl),65 ;(ascii de "A" sola) 201 ret soluciona un caso en particular. Es posible que creemos una variable de cadena de nombre A$, pero la salvemos con SAVE "nombre" DATA a$(), si la cargamos despues, nos daremos conque no es posible usarla a pesar de que la recuperamos exitosamente. La razon es que ha sido salvada y tratada como matriz en vez de como una variable alfanumerica normal. El Spectrum almacena las matrices en su memoria por su codigo ASCII mas 128, de ahi que se guarde como 193. Para recuperarla debemos quitarle ese 128 y aqui es donde interviene esta rutina, la cual es reubicable. Sin embargo hay que modificar el dato del codigo ascii si se tratase de otro nombre de variable. Así que mi solucion es: 100 LET X=50000: FOR N=X TO X+17 120 READ NN: POKE N,NN: NEXT N 200 INPUT "Nombre de la variable de cadena ";X$ 210 LET XX=CODE X$: POKE X+5,XX+128: POKE X+15,XX 220 PRINT VAL$(X$+"$") (c)2022 zx_if1@hotmail.com