Usa este documento como una guía de referencia rápida de todas las funciones del lenguaje de programación Verse y su sintaxis. Sigue los enlaces para obtener más información.
Expresiones
Una expresión es la unidad de código más pequeña que produce un valor cuando se evalúa. Un ejemplo es una expresión if… else
, que en Verse se evalúa como un valor que depende del contenido de los bloques de expresión.
El siguiente código se evalúa como un valor de cadena que contiene "Big!"
o "Small!"
, según si MyNumber
era mayor que 5:
if (MyNumber > 5):
"Big!"
else:
"Small!"
Comentarios de código
En un comentario de código, se explica algo sobre el código, o la razón por la cual el programador programó algo de esa manera. Cuando se ejecuta el programa se ignoran los comentarios de código.
Verse
| Comentario de una sola línea: todo lo que aparezca entre |
Verse
| Comentario de bloque en línea: todo lo que aparezca entre |
Verse
| Comentario de bloque multilínea: todo lo que aparezca entre |
Verse
| Comentario de bloque anidado: todo lo que aparezca entre |
Verse
| Comentario indentado: todo lo que aparece en líneas nuevas después de |
Constantes y variables
Las constantes y las variables pueden almacenar información, o valores, que utiliza el programa, y asociar estos valores a un nombre. El nombre es el identificador.
Para crear tu propia variable o constante, tienes que decírselo a Verse. Esto se denomina una declaración. Especificar un valor inicial, denominado inicialización, es opcional para las variables (aunque se recomienda) pero obligatorio para las constantes.
Puedes cambiar el valor de una variable en cualquier momento. Esto se denomina formalmente asignación porque estás asignando un valor a una variable pero, a veces, también se denomina definición de la variable.
Verse
| Cómo crear una constante: el valor de una constante no se puede cambiar mientras el programa está en ejecución. Creas una constante al especificar su nombre, tipo y valor. | Haz clic en la imagen para ampliarla. |
Verse
| Cómo crear una variable: el valor de una variable puede cambiarse mientras el programa se ejecuta. Para crear una variable, añade la palabra clave | Haz clic en la imagen para ampliarla. |
Verse
| Cómo cambiar el valor de una variable: puedes cambiar el valor de una variable mientras el programa está en ejecución mediante la palabra clave | Haz clic en la imagen para ampliarla. |
Tipos
Verse es un lenguaje de programación de tipado estático, es decir, se asigna un tipo a cada identificador.
Existen casos en los que el tipo no se requiere explícitamente, como al crear una constante en una función. En el ejemplo MyConstant := 0
, el tipo de MyConstant
se infiere porque se le asigna el valor 0.
Tipos comunes
Verse tiene tipos integrados que admiten las operaciones fundamentales que necesita realizar la mayoría de los programas. Puedes crear tus propios tipos combinándolos en estructuras más grandes, pero es importante entender estos tipos comunes como base para el uso de variables y constantes en Verse.
Verse
| logic: en Verse, un tipo |
Verse
| int: en Verse, un
|
Verse
| float: Un
|
Verse
| string: un Puedes inyectar el resultado de una expresión en una cadena con {} dentro de "". Esto se denomina interpolación de cadena. Para obtener más información sobre el tipo |
Verse
| message: un Puedes convertir el texto en una cadena que puede mostrarse en el tiempo de ejecución si usas la función Actualmente, el único texto que puede devolver la función |
Verse
| locale: este tipo representa en qué contexto un valor Actualmente, el tipo |
Verse
| rational: el tipo
|
Verse
| void: el tipo
Para obtener más información sobre el tipo |
Verse
| any: el tipo |
Verse
| comparable: el tipo |
Para aprender más sobre los tipos comunes en Verse, consulta Tipos comunes.
Tipos de contenedores
Puedes almacenar varios valores juntos si utilizas un tipo de contenedor. Verse dispone de varios tipos de contenedor donde almacenar valores. Para obtener más información sobre los tipos de contenedor en Verse, consulta Tipos de contenedor.
Opción
El tipo option
puede contener un valor o puede estar vacío.
En el siguiente ejemplo, MaybeANumber
es un entero opcional ?int
sin valor. Después, se define un nuevo valor para MaybeANumber
en 42
.
var MaybeANumber : ?int = false # unset optional value
set MaybeANumber := option{42} # assigned the value 42
Haz clic en la imagen para ampliarla.
Verse
| Crear una opción: puedes inicializar una opción con alguno de los siguientes:
Para especificar el tipo, añade |
Verse
| Acceder a un elemento en una opción: usa el operador de consulta |
El siguiente es un ejemplo del uso de un tipo opción para guardar una referencia a un jugador generado y, cuando se genera el jugador, hacer que el dispositivo activador reaccione:
my_device := class<concrete>(creative_device):
var SavedPlayer : ?player = false # unset optional value
@editable
PlayerSpawn : player_spawner_device = player_spawner_device{}
@editable
Trigger : trigger_device = trigger_device{}
OnBegin<override>() : void =
Rango
La expresión de rango contiene todos los números de un rango determinado y solo puede utilizarse en expresiones específicas como la expresión for. Los valores del rango solo pueden ser enteros.
Haz clic en la imagen para ampliarla.
Verse
| Crear un rango: el inicio del rango es el primer valor de la expresión y el final del rango es el valor que sigue a |
Verse
| Iterar un rango: puedes usar la expresión |
Para más información, consulta Rango.
Array
Una matriz es un contenedor donde puedes almacenar elementos del mismo tipo y acceder a ellos por su posición, denominado índice, en la matriz. El primer índice en una matriz es 0 y el último es uno menos del número de elementos de la matriz.
Players : []player = array{Player1, Player2}
Haz clic en la imagen para ampliarla.
Verse
| Cómo crear una matriz: usa la palabra clave |
Verse
| Cómo acceder a un elemento en una matriz: usa |
Verse
| Cómo cambiar un elemento en una matriz: puedes cambiar el valor almacenado en una variable de la matriz en un índice si usas la palabra clave |
Verse
| Cómo iterar una matriz: puedes acceder a todos los elementos en una matriz, en orden, desde el primero al último, mediante la expresión for. Con la expresión |
Verse
| Cómo obtener el número de elementos en una matriz: usa |
Verse
| Cómo concatenar matrices: puedes fusionar matrices con el operador |
Para obtener más información, consulta Matriz.
tupla
Una tupla es la agrupación de dos o más expresiones que se consideran como una única expresión. El orden de los elementos de una expresión es importante. La misma expresión puede estar en múltiples ubicaciones en una tupla. Las expresiones de tupla pueden ser de cualquier tipo y pueden tener tipos mixtos (a diferencia de las matrices que solo pueden tener elementos de un tipo).
Las tuplas son especialmente útiles para:
Devolver varios valores desde una función.
Pasar múltiples valores a una función.
Las agrupaciones en el lugar es más conciso que el trabajo de realizar una estructura de datos reutilizable (como una struct o class).
VerseExampleTuple : tuple(int, float, string) = (1, 2.0, "three")
Haz clic en la imagen para ampliarla.
Verse
| Cómo crear una tupla: usa |
Cómo acceder a un elemento en una tupla: usa | |
Expansión de tupla: cuando usas una tupla como un argumento a una función en lugar de especificar cada argumento individual, es posible llamar a la función con cada elemento de la tupla que se usa como argumento en el orden en que están especificados en la tupla. | |
Conversión de matriz de tupla: es posible pasar tuplas cuando se espere una matriz, siempre que los elementos de la tupla sean todos del mismo tipo que la matriz. No se pueden pasar matrices cuando se espera una tupla. |
Para obtener más información, consulta Tupla.
Mapa
Un mapa es un contendor donde puedes guardar los valores asociados con otro valor, denominados pares clave-valor y acceder a los elementos mediante sus claves únicas.
WordCount : [string]int = map {"apple" => 11, "pear" => 7}
Haz clic en la imagen para ampliarla.
Cómo crear un mapa: utiliza la palabra clave | |
Acceder a un elemento en un mapa: usa | |
Iterar un mapa: puedes acceder a cada elemento en un mapa, en orden, del primer elemento insertado al último, mediante la expresión for. Con la expresión | |
Cómo obtener el número de pares clave-valor en un mapa: usa |
Para obtener más información, consulta Mapa.
Tipos compuestos
Un tipo compuesto es todo tipo que puede componerse de campos y elementos (usualmente con nombre) de tipos primitivos o de otra clase. Los tipos compuestos usualmente tienen un número fijo de campos o elementos para su vida útil. Para obtener más información, consulta Tipos compuestos.
Enum
Enum es la forma abreviada de enumeración, que significa nombrar o enumerar una serie de cosas, que se denominan enumeradores. En Verse, es un tipo que puede usarse para cosas como días de la semana o direcciones de una brújula.
Haz clic en la imagen para ampliarla.
Cómo crear una enum: usa la palabra clave | |
Acceder a un enumerador: usa |
Estructura
Struct es la abreviatura de structure (estructura) y es una manera de agrupar varias variables relacionadas. Las variables pueden ser de cualquier tipo.
Haz clic en la imagen para ampliarla.
Haz clic en la imagen para ampliarla.
Cómo crear una estructura: usa la palabra clave | |
Cómo instanciar una struct: puedes construir una instancia de una struct a partir de un arquetipo. Un arquetipo define los valores de los campos de una struct. | |
Cómo acceder a los campos en una struct: puedes acceder al campo de una struct para obtener su valor al añadir |
Clase
Una clase es una plantilla para crear objetos con comportamientos y propiedades (variables y métodos) similares y debe instanciarse para crear un objeto con valores reales. Las clases son jerárquicas, es decir, la clase puede heredar información de su base (superclase) y compartir su información con sus derivadas (subclases). Las clases pueden ser un tipo personalizado definido por el usuario.
Haz clic en la imagen para ampliarla.
Haz clic en la imagen para ampliarla.
Cómo crear una clase: las definiciones anidadas dentro de una clase definen los campos de la clase. Las funciones definidas como campos de una clase también se denominan métodos. | |
Cómo instanciar una clase: puedes construir una instancia de la clase con el nombre de la clase seguido de | |
Cómo acceder a los campos en una clase: puedes acceder al campo de una clase para obtener su valor al añadir | |
Cómo acceder a los métodos de una clase: puedes acceder a los métodos de una clase para llamarlos al añadir | |
Self: puedes usar |
Existen, también, especificadores que son exclusivos de clases y que cambian su comportamiento. Consulta Especificadores de clase para obtener más detalles.
Para obtener más información sobre las clases, consulta Clase.
Subclase
Una subclase es una clase que amplía la definición de otra clase al añadir o modificar los campos y métodos de la otra clase (llamada la superclase).
Haz clic en la imagen para ampliarla.
Cómo crear una subclase: especifica una clase como subclase de otra añadiendo la otra clase entre | |
Cómo anular campos en la superclase: puedes cambiar los valores de un campo definido en la superclase para la subclase simplemente añadiendo el especificador | |
Cómo anular métodos en la superclase: [INCLUDE:#overriding_methods_on_superclass_description] | |
Super: al igual que con |
Para obtener más información, consulta Subclass.
Constructor
Un constructor es una función especial que crea una instancia de la clase a la que está asociado. Puede usarse para definir los valores iniciales de un nuevo objeto.
Cómo definir un constructor para una clase: puedes añadir un constructor para una clase si añades el especificador | |
Cómo añadir variables y ejecutar código en el constructor: puedes ejecutar expresiones dentro de un constructor con la expresión block e introducir nuevas variables con la palabra clave | |
Llamar a otros constructores en un constructor: puedes llamar a otros constructores desde un constructor. También puedes llamar constructores de la superclase de la clase desde un constructor de la clase siempre que todos los campos estén inicializados. Cuando un constructor llama a otro constructor y ambos constructores inicializan campos, solo los valores proporcionados al primer constructor se usan en los campos. El orden de evaluación de las expresiones entre los dos constructores será el orden en que están escritas las expresiones (en lo que respecta a efectos secundarios) pero solo se usarán los valores proporcionados al primer constructor. |
Interfaz
El tipo interfaz proporciona un contrato sobre cómo interactuar con cualquier clase que implementa una interfaz. Una interfaz no puede instanciarse, pero una clase puede heredar de la interfaz e implementar sus métodos. Una interfaz es similar a una clase abstracta, excepto que no permite una implementación parcial o campos como parte de la definición.
Haz clic en la imagen para ampliarla.
Haz clic en la imagen para ampliarla.
Cómo crear una interfaz: se define una interfaz de manera similar a una clase, excepto con la palabra clave | |
Cómo extender una interfaz: una interfaz puede extender la definición de otra interfaz al especificar la interfaz a extender entre | |
Cómo implementar una interfaz: puedes implementar una interfaz con una clase especificando la interfaz entre | |
Cómo implementar varias interfaces: una clase puede implementar varias interfaces. Las interfaces se separan con | |
Cómo heredar de una interfaz a otra clase: una clase puede implementar una interfaz y ser una subclase de otra clase. La interfaz y la superclase se separan con |
Para obtener más información, consulta Interfaz.
Cómo trabajar con tipos
Verse ofrece algunas maneras de facilitar el trabajo con tipos.
Alias de tipo: puedes otorgar a un tipo un nombre diferente en el código y hacer referencia al tipo con el nuevo nombre. La sintaxis es similar a la inicialización de constantes. También puedes otorgar un alias de tipo a un tipo de función. Para más información, consulta Solapamiento. | |
Tipo paramétrico como tipos de argumentos explícitos: Verse admite el tipo paramétrico (tipos que se esperan como argumento). Esto funciona solo con clases y funciones. Para obtener más información, consulta Tipos paramétricos. | |
Tipo paramétrico como argumento de tipo implícito a funciones: el motivo para usar tipos paramétricos implícitos con funciones es que permite escribir código invariable de un tipo particular en lugar de para cada tipo con el que se usa la función. Para obtener más información, consulta Tipos paramétricos. | |
Macro de tipo: Verse tiene una construcción especial que puede usarse para obtener el tipo de una expresión arbitraria. Puede usarse en cualquier lugar donde se pueda utilizarse un tipo. Para más información, consulta Macro de tipo. | |
Subtipo: puedes usar |
Para obtener más información, consulta Trabajar con tipos en Verse.
Operadores
Los operadores son funciones especiales definidas en el lenguaje de programación Verse que realizan acciones, como operaciones matemáticas, en sus operandos.
Cuando se utilizan varios operadores en la misma expresión, se evalúan en orden de mayor a menor prelación. Si hay operadores con la misma prelación en la misma expresión, se evalúan de izquierda a derecha.
La siguiente tabla enumera todos los operadores incorporados en Verse de mayor a menor prelación.
Operador | Descripción | Formato del operador | prelación de operadores | Ejemplo |
---|---|---|---|---|
Pregunta | El operador | posfijo | 9 |
|
No | El operador | Prefijo | 8 |
|
Positivo | Puedes usar el operador | Prefijo | 8 |
|
Negativo | Puedes usar el operador | Prefijo | 8 |
|
Multiplicación | El | infijo | 7 |
|
División | El operador | infijo | 7 |
|
Suma | El operador + suma dos valores numéricos. Cuando se utiliza con cadenas y matrices, los dos valores se concatenan. Consulta Matemáticas para obtener más detalles. | infijo | 6 |
|
Resta | El operador | infijo | 6 |
|
Asignación de suma | Con este operador, puedes combinar la suma y la asignación en la misma operación para actualizar el valor de una variable. Consulta Matemáticas para obtener más detalles. | infijo | 5 |
|
Asignación de resta | Con este operador, puedes combinar la resta y la asignación en la misma operación para actualizar el valor de una variable. Consulta Matemáticas para obtener más detalles. | infijo | 5 |
|
Asignación de multiplicación | Con este operador, puedes combinar la multiplicación y la asignación en la misma operación para actualizar el valor de una variable. Consulta Matemáticas para obtener más detalles. | infijo | 5 |
|
Asignación de división | Con este operador, puedes combinar la división y la asignación en la misma operación para actualizar el valor de una variable, a menos que la variable sea un entero. Consulta Matemáticas para obtener más detalles. | infijo | 5 |
|
Es igual a | El operador | infijo | 4 |
|
Distinto de | El operador | infijo | 4 |
|
Menor que | El operador | infijo | 4 |
|
Menor o igual que | El operador | infijo | 4 |
|
Mayor que | El operador | infijo | 4 |
|
Mayor o igual que | El operador | infijo | 4 |
|
Y | El operador | infijo | 3 |
|
O | El operador | infijo | 2 |
|
Inicialización de variables y constantes | Con este operador, puedes almacenar valores en una constante o en una variable. Consulta Constantes y variables para obtener más información. | infijo | 1 |
|
Asignación de variables | Con este operador puedes actualizar los valores almacenados en una variable. Consulta Constantes y variables para obtener más información. | infijo | 1 |
|
Para obtener más información, consulta Operadores.
Grupo
Puedes cambiar el orden de evaluación de los operadores agrupando las expresiones con ()
. Por ejemplo, (1+2)*3
y 1+(2*3)
no se evalúan al mismo valor porque las expresiones agrupadas entre () se evaluarán primero.
El siguiente ejemplo muestra cómo usar agrupación para calcular una explosión en el juego que escala su daño en función de la distancia del jugador, pero donde la armadura del jugador puede reducir el daño total:
BaseDamage : float = 100.0
Armor : float = 15.0
DistanceScaling : float = Max(1.0, Pow(PlayerDistance, 2.0))
ExplosionDamage : float = Max(0.0, (BaseDamage / DistanceScaling) - Armor)
Consulta Agrupación para obtener más detalles.
Bloques de código
Un bloque de código es un grupo de cero o más expresiones e introduce un nuevo cuerpo de ámbito. Un bloque de código debe seguir un identificador. Puede ser un identificador de función o un identificador de flujo de control como if y for, por ejemplo.
Espaciado: comienza con | |
Multilínea entre llaves: encerrado entre | |
Línea única entre llaves: encerrado entre |
También es posible usar ;
para poner más de una expresión en una línea. En un formato que tiene cada expresión en una nueva línea, no es necesario que los caracteres {}
estén en sus propias líneas.
La última expresión del bloque de código es el resultado del bloque de código. En el siguiente ejemplo, el bloque de código de la expresión if
resulta en false
, si IsLightOn?
tiene éxito, o true
, si IsLightOn?
falla. El resultado logic
se almacena en NewLightState
.
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
Para obtener más información, consulta Bloques de código.
Ámbito
Ámbito hace referencia al código dentro de un programa en Verse donde la asociación de un identificador (nombre) y un valor es válida y donde puede usarse dicho identificador para hacer referencia al valor.
Por ejemplo, cualquier constante o variable que creas dentro de un bloque de código existe solo en el contexto de dicho bloque de código. Esto significa que la vida útil de los objetos está limitada al ámbito en el que fueron creados y no pueden usarse fuera de dicho bloque de código.
El siguiente ejemplo muestra cómo calcular el número máximo de flechas que pueden comprarse con el número de monedas que tiene el jugador. Se crea la constante MaxArrowsYouCanBuy
dentro del bloque if
; por lo tanto, el ámbito está limitado al bloque if
. Cuando se usa la constante MaxArrowsYouCanBuy
en la cadena impresa, produce un error porque el nombre MaxArrowsYouCanBuy
no existe en el ámbito fuera de la expresión if
.
CoinsPerQuiver : int = 100
ArrowsPerQuiver : int = 15
var Coins : int = 225
if (MaxQuiversYouCanBuy : int = Floor(Coins / CoinsPerQuiver)):
MaxArrowsYouCanBuy : int = MaxQuiversYouCanBuy * ArrowsPerQuiver
Print("You can buy at most {MaxArrowsYouCanBuy} arrows with your coins.") # Error: Unknown identifier MaxArrowsYouCanBuy
Haz clic en la imagen para ampliarla.
Verse no admite reutilizar un identificador incluso si está declarado en un ámbito diferente a menos que califiques el identificador al añadir (qualifying_scope:)
antes del identificador, donde qualifying_scope
es el nombre del módulo, clase o interfaz de un identificador. Siempre que definas y uses un identificador, también debes añadir el calificador al identificador.
Funciones
Una función es una secuencia de expresiones con nombre que puedes reutilizar. Las funciones proporcionan instrucciones para realizar una acción o crear una salida con base en una entrada.
Definiciones de función
Para definir tu propia función, debes proporcionar tres partes clave: un nombre único (identificador), el tipo de información que esperas como resultado y qué hará la función cuando la llames. La siguiente es la sintaxis básica de una función:
name() : type =
codeblock
Name() y tipo separados por dos puntos: esta es la firma de función que es cómo debes llamar y usar la función y el valor que debe devolver la función es del tipo que proporcionas. Este formato es similar a como creas constantes excepto por () después del nombre, similar a como llamas a una función en el código.
El bloque de código de la función: defines lo que hará la función cuando se la llame con
=codeblock
, dondecodeblock
es cualquier secuencia de una o más expresiones. Siempre que llames a la función, se ejecutarán las expresiones del bloque de código.
Haz clic en la imagen para ampliarla.
Resultados de la función
Cuando la función tiene un tipo de devolución especificado, el cuerpo de la función debe producir un resultado de dicho tipo o el código no se compilará.
Última expresión devuelta con un valor: de manera predeterminada, la última expresión en el bloque de código de la función es el resultado de dicha función cuyo valor debe coincidir con el tipo de devolución de la función. | |
Devolución explícita con un valor: también puedes definir explícitamente lo que devolverá la función mediante |
Cuando creas una función que no necesita producir un resultado, puedes definir el tipo de devolución de la función en void, es decir, no se espera que la función produzca un resultado útil por lo que la última expresión en el bloque de código de la función puede ser de cualquier tipo.
Puedes salir de una función cuyo tipo de devolución sea void
mediante una expresión return
en sí misma. Esta expresión sale inmediatamente de la función, incluso si hay más expresiones después de ella en el bloque de código.
Parámetros de función
La entrada a una función se define mediante parámetros. Un parámetro es una constante que se declara en la firma de función entre paréntesis y que, más adelante, puedes usar en el cuerpo de una función.
La siguiente es la sintaxis de una función con dos parámetros:
name(parameter1name : type, parameter2name : type) : type =
codeblock
Haz clic en la imagen para ampliarla.
En el siguiente ejemplo, la función IncreaseScore()
tiene un parámetro entero denominado Points
que usa la función para aumentar el valor de MyScore
:
var MyScore : int = 100
IncreaseScore(Points : int) : void =
# Increase MyScore by Points.
set MyScore = MyScore + Points
Llamadas a función
Cuando deseas usar la secuencia de expresiones con nombre (la función) en el código, llamarás a la función por su nombre, como, por ejemplo, GetRandomInt(1, 10)
, que devuelve un entero aleatorio entre 1 y 10, inclusive.
Existen dos maneras de llamar a una función dependiendo de si la llamada a función es falible:
Llamada a función no falible: una llamada a función que no puede fallar tiene la forma | |
Llamada a función falible: una llamada a función falible tiene la forma |
Argumentos de función
Cuando llamas a una función que espera parámetros, debes asignar valores a los parámetros, igual que necesitas asignar valores a las constantes para poder utilizarlas. Los valores asignados se denominan argumentos a la función.
La siguiente es la sintaxis para llamar a una función con dos argumentos:
name(parameter1name := value, parameter2name := value)
En el siguiente ejemplo, se llama a la función IncreaseScore()
tres veces con distintos argumentos cada vez (10, 5 y 20) para aumentar el valor de MyScore
:
# After this call, MyScore is 110
IncreaseScore(Points := 10)
# After this call, MyScore is 115
IncreaseScore(Points := 5)
# After this call, MyScore is 135
IncreaseScore(Points := 20)
Métodos de extensión
Los métodos de extensión son un tipo de función que actúan como miembros de una clase o tipo existente, pero que no requieren la creación de un nuevo tipo o subclase.
A continuación, se muestra cómo crear un método de extensión para matrices de tipo int
. El método suma todos los números de la matriz y devuelve el total.
# Sum extension method for type []int
(Arr : []int).Sum<public>() : int =
var Total : int = 0
for (Number : Arr):
set Total = Total + Number
return Total
El método puede llamarse sobre cualquier matriz de tipo int
.
SumTotal := array{4, 3, 7}.Sum()
Print("The SumTotal is { SumTotal }")
# "The SumTotal is 14"
Falla
A diferencia de otros lenguajes de programación que usan los valores booleanos verdadero y falso para cambiar el flujo de un programa, Verse usa expresiones que pueden tener éxito o fallar. Estas expresiones se denominan expresiones falibles y solo pueden ejecutarse en un contexto de fallo.
Expresiones falibles
Una expresión falible es una expresión que puede tener éxito y producir un valor o fallar y no producir un valor.
La siguiente lista incluye todas las expresiones falibles en Verse:
Llamada a función: solo cuando la llamada a la función tiene la forma | |
Comparación: una expresión de comparación compara dos cosas con alguno de los operadores de comparación. Si deseas obtener más información, consulta Operadores. | |
División de enteros: en el caso de los enteros, el operador de división | |
Decisión: una expresión de decisión utiliza los operadores | |
Consulta: una expresión de consulta utiliza el operador | |
Acceder a un elemento en una matriz: acceder al valor almacenado en una matriz es una expresión falible porque es posible que no haya un elemento en dicho índice por lo que debe usarse en un contexto de fallo. Para obtener más información, consulta Matriz. |
Contextos de fallo
Un contexto de fallo es un contexto donde se permite ejecutar expresiones falibles. El contexto define lo que ocurre si la expresión falla. Cualquier error en un contexto de fallo provocará errores en todo el contexto.
Un contexto de error permite que las expresiones anidadas sean expresiones de error, como expresiones o argumentos de función en una expresión block.
La siguiente lista incluye todos los contextos de fallo en Verse:
La condición en expresiones | |
Las expresiones de iteración y de filtro en las expresiones | |
El cuerpo de una función con el especificador de efecto | |
El operando para el operador | |
El operando izquierdo para el operador | |
Inicializando una variable que tiene el tipo |
ejecución especulativa
Un aspecto útil de los contextos de fallo en Verse es que son una forma de ejecución especulativa, lo que significa que puedes probar acciones sin confirmarlas. Cuando una expresión tiene éxito, los efectos de la expresión se confirman, como cambiar el valor de una variable. Si la expresión falla, los efectos de la expresión se revierten, como si la expresión nunca hubiera ocurrido.
De esta manera, puedes ejecutar una serie de acciones que acumulen cambios, pero esas acciones se desharán si fallan en algún punto en el contexto de fallo.
Para que esto funcione, todas las funciones llamadas en el contexto de fallo deben tener el especificador de efecto transacts
.
Especificadores
Los especificadores en Verse describen el comportamiento en relación a la semántica y pueden añadirse a identificadores y determinadas palabras clave. La sintaxis de los especificadores usan <
y >
con la palabra clave entre ellos, como, por ejemplo, IsPuzzleSolved()<decides><transacts> : void
.
La siguiente sección describe todos los especificadores en Verse y cuándo puedes usarlos.
Especificadores de efecto
Los efectos en Verse indican categorías de comportamiento que puede mostrar una función. Puedes añadir especificadores de efecto a:
Los () después del nombre en la definición de una función:
name()<specifier> : type = codeblock
.La palabra clave
class
:name := class<specifier>():
.
Los especificadores de efecto se dividen en dos categorías:
Exclusivo: puedes tener solo uno o ningún especificador de efecto exclusivo añadido a una función o la palabra clave
class
. Si no se ha añadido ningún especificador de efecto exclusivo, el efecto predeterminado esno_rollback
.Aditivo: puedes añadir todos, algunos o ningún especificador de efecto aditivo a una función o la palabra clave
class
.
Ejemplo | Efecto |
---|---|
no_rollback: este es el efecto predeterminado cuando no se especifica ningún efecto exclusivo. El efecto | |
Efectos exclusivos | |
transacts: este efecto indica que cualquier acción realizada por la función puede revertirse. El efecto transacts es obligatorio siempre que se escriba una variable mutable ( | |
varies: este efecto indica que la misma entrada a la función puede no siempre producir la misma salida. El efecto | |
computes: este efecto requiere que la función no tenga efectos secundarios y no se garantiza que se complete. Existe un requisito no verificado que la función, cuando se proporcionen los mismos argumentos, produzca el mismo resultado. Cualquier función que no tenga el especificador native y, que de lo contrario, tenga el efecto | |
converges: este efecto garantiza que no solo no hay efectos secundarios de la ejecución de la función relacionada sino que la función se completa de forma definitiva (no se repite infinitamente). Este efecto solo puede aparecer en funciones que tengan el especificador nativo, pero el compilador no lo comprueba. El código que se use para proporcionar valores predeterminados de clase o valores de variables globales debe tener este efecto. Las funciones | |
Efectos aditivos | |
decides: indica que la función puede fallar y que llamar a esta función es una expresión falible. Las definiciones de función con el efecto | |
suspends: indica que la función es asíncrona. Crea un contexto asíncrono para el cuerpo de la función. |
En todos los casos, llamar a una función que tiene un efecto específico requerirá que también el llamador tenga ese efecto.
Especificadores de acceso
Los especificadores de acceso definen qué puede interactuar con un miembro y cómo. Los especificadores de acceso pueden aplicarse a lo siguiente:
El identificador de un miembro:
name<specifier> : type = value
La palabra clave
var
para un miembro:var<specifier> name : type = value
Especificadores de clase
Los especificadores de clase definen determinadas características de las clases o sus miembros, como, por ejemplo, si se puede crear una subclase de una clase.
abstract: cuando una clase o un método de clase tiene el especificador abstract, no se puede crear una instancia de la clase. La finalidad de las clases abstractas es usarlas como superclases con una implementación parcial o como una interfaz común. Esto es útil cuando no tiene sentido tener instancias de una superclase pero no deseas duplicar las propiedades y comportamientos en todas las clases similares. | |
concrete: cuando una clase tiene el especificador concrete, debe ser posible construirla con un arquetipo vacío; es decir, todos los campos de la clase deben tener un valor predeterminado. Todas las subclases de una clase concreta son implícitamente concretas. Una clase concreta solo puede heredar directamente de una clase abstracta si ambas clases están definidas en el mismo módulo. | |
unique: el especificador unique se puede aplicar a una clase para convertirla en una clase única. Para construir una instancia de una clase única, Verse asigna una identidad única para la instancia resultante. Esto permite comparar la igualdad de las instancias de clases únicas mediante la comparación de sus identidades. Las clases sin el identificador unique no tienen dicha identidad y su igualdad solo pueden compararse con base en los valores de sus campos. Esto significa que las clases únicas pueden compararse con los operadores = y <> y son subtipos del tipo comparable. |
Especificadores de implementación
No es posible usar especificadores de implementación al escribir código pero los verás en las API de UEFN.
native_callable: indica que un método de instancia es nativo (implementado en C++) y que otro código de C++ podría llamarlo. Puedes ver este especificador usado en un método de instancia. Este especificador no se propaga a subclases por lo que no es necesario que lo añadas a una definición cuando anules el método que tiene este especificador. |
Especificador de localización
Debes usar el especificador localizes
cuando vas a definir un mensaje nuevo. De manera específica, se trata de cuando la variable tiene el tipo message y vas a inicializar la variable con un valor string.
# The winning player's name:
PlayerName<localizes> : message = "Player One"
# Build a message announcing the winner.
Announcement<localizes>(WinningPlayerName : string) : message = "...And the winner is: {WinningPlayerName}"
Billboard.SetText(Announcement("Player One"))
No tienes que usar el especificador localizes
cuando inicializas un valor de miembro con un mensaje ya creado porque el especificador localizes
solo es para definir mensajes nuevos.
PlayerOne<localizes> : message = "Player One"
# The winning player's name:
PlayerName : message = PlayerOne
Atributos
En Verse, los atributos describen el comportamiento que se usa fuera del lenguaje Verse (a diferencia de los especificadores, que describen la semántica de Verse). Los atributos pueden añadirse en la línea de código antes de las definiciones.
La sintaxis de atributos usa @
seguida de la palabra clave.
flujo de control
El flujo de control es el orden en que una computadora ejecuta instrucciones. Verse tiene distintas expresiones que puedes usar para controlar el flujo del programa.
Bloquear
Debido a que Verse requiere un identificador antes de un bloque de código, las expresiones de block
son la manera de anidar bloques de código. Un bloque de código anidado se comporta de manera similar que un bloque de código. Como en el caso de los bloques de código, una expresión de block
introduce un nuevo cuerpo de ámbito anidado.
bloque |
---|
Resultado: última expresión en el bloque de código |
Para obtener más información, consulta Block.
Si
Con la expresión if
puedes tomar decisiones que cambian el flujo del programa. Como en otros lenguajes de programación, la expresión if
en Verse admite ejecuciones condicionales pero, en Verse, las condiciones usan éxito o fracaso para impulsar la decisión.
Si | if… Then |
---|---|
Resultado: última expresión en el bloque de código | Resultado: última expresión en el bloque de código |
if… else | if… else if… else |
---|
En el siguiente ejemplo, el bloque de código de la expresión if
resulta en false
, si IsLightOn?
tiene éxito, o true
, si IsLightOn?
falla. El resultado logic
se almacena en NewLightState
.
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
Un caso útil de la expresión if
en Verse es que puedes probar expresiones falibles y, si fallan, las acciones se revierten como si nunca hubieran ocurrido. Si deseas más información sobre esta función, consulta Ejecución especulativa.
Para obtener más información, consulta If.
Case
Con las expresiones case
puedes controlar el flujo de un programa con una lista de opciones. La declaración `case` en Verse te permite probar un valor contra múltiple valores posibles (como si usaras =) y ejecutar el código con base en cuál coincide.
estuche |
---|
Para obtener más información, consulta Case.
Bucle
Con la expresión loop
, las expresiones en el bloque de bucle se repiten en cada iteración del bucle.
loop |
---|
Resultado: el resultado de la expresión |
En el siguiente ejemplo, una plataforma aparece y desaparece cada ToggleDelay
segundos durante toda la ejecución del juego.
loop:
Sleep(ToggleDelay) # Sleep(ToggleDelay) waits for ToggleDelay seconds before proceeding to the next instruction.
Platform.Hide()
Sleep(ToggleDelay)
Platform.Show() # The loop restarts immediately, calling Sleep(ToggleDelay) again.
Para obtener más información, consulta Loop.
Cómo detener bucles
Un bloque de bucle se repetirá para siempre; para detener el bucle, puedes salir del bucle con break
o con una expresión return de una función.
Loop y break (bucle e interrupción) | bucle y devolución |
---|---|
Resultado: el resultado de la expresión | Resultado: el resultado de la expresión |
bucle e interrupción anidado |
---|
Resultado: el resultado de la expresión |
Para más información, consulta Loop y break.
For
Una expresión for
, a veces denominada for loop (bucle for), es lo mismo que una expresión loop excepto que las expresiones for
usan un generador para producir una secuencia de valores, uno a la vez, y le otorgan un nombre a cada valor.
Por ejemplo, la expresión for(Value : 1..3)
produce una secuencia 1, 2, 3 y se otorga a cada número en la secuencia el nombre Value
para cada iteración de manera que el bucle for
se ejecuta tres veces.
La expresión for
contiene dos partes:
Especificación de iteración: las expresiones dentro del paréntesis. La primera expresión debe ser un generador, pero las otras expresiones pueden ser una inicialización de constante o un filtro.
Cuerpo: las expresiones en el bloque de código después del paréntesis.
para | for con un filtro |
---|---|
Resultado: el resultado de una expresión | Resultado: el resultado de una expresión |
for con varios generadores | anidado para bloques |
---|---|
Resultado: el resultado de una expresión | Resultado: el resultado de una expresión |
Para obtener más información, consulta For.
diferir
Una expresión defer
se ejecuta justo antes de transferir el control del programa fuera del ámbito en el que aparece la expresión defer
, incluida cualquier expresión de resultado, como en return
. No importa cómo se transfiere el control del programa.
diferir | defer antes de una salida |
---|---|
Resultado: la expresión | Resultado: la expresión |
Una expresión defer
no se ejecutará si se produce una salida anterior antes de encontrar defer.
defer con devolución temprana | defer con una tarea concurrente cancelada |
---|---|
Resultado: la expresión | Resultado: la expresión |
Múltiples expresiones defer
que aparecen en el mismo ámbito se acumulan. El orden en el que se ejecutan es el orden inverso en la que se encuentran, orden FILO (siglas en inglés de primero en entrar último en salir). Debido a que el último defer
encontrado en un ámbito determinado se ejecuta primero, las expresiones dentro del último defer
encontrado pueden referirse a contexto ya borrado por otras expresiones defer
encontradas anteriormente y ejecutadas más tarde.
Múltiples expresiones defer en un bloque de código | Múltiples expresiones defer en diferentes bloques de código |
---|---|
Resultado: la expresión | Resultado: la expresión |
Flujo de tiempo y concurrencia
El control de flujo de tiempo es el centro del lenguaje de programación Verse y se logra con expresiones concurrentes. Para aprender más sobre la concurrencia, consulta Acerca de la concurrencia.
concurrencia estructurada
Las expresiones de concurrencia estructurada se utilizan para especificar flujo de tiempo asíncrono y para modificar la naturaleza de bloque de las expresiones asíncronas con vida útil restringida a un ámbito de contexto asíncrono específico (como un cuerpo de función asíncrona).
Esto es similar al flujo de control estructurado como block
, if
, for
y loop
que se restringen a sus ámbitos asociados.
sync (sincronizar) | rama |
---|---|
Ejecuta concurrentemente todas las expresiones en el bloque de código y espera a que todas finalicen antes de ejecutar la siguiente expresión después de | El cuerpo de la expresión |
Resultado: el resultado de una expresión | Resultado: una expresión |
race | rush |
---|---|
Similar a | Similar a |
Resultado: el resultado de una expresión | Resultado: el resultado de una expresión |
concurrencia no estructurada
De las expresiones de concurrencia no estructurada, la única es spawn (generar). Tienen una vida útil no restringida a un ámbito de contexto asíncrono y, potencialmente, se extienden fuera del ámbito donde se ejecutan.
Generar |
---|
El cuerpo de una |
Resultado: una |
Tarea
Una tarea es un objeto que se utiliza para representar el estado de una función asíncrona actualmente en ejecución. Los objetos de tarea se utilizan para identificar dónde se suspende una función asíncrona y los valores de las variables locales en el punto de suspensión.
# Get task to query / give commands to
# starts and continues independently
Task2 := spawn{Player.MoveTo(Target1)}
Task2.Await() # wait until MoveTo() completed
Módulos y rutas
Un módulo de Verse es una unidad atómica de código que puede redistribuirse y de la que se puede depender y puede evolucionar en el tiempo sin romper dependencias. Puedes importar un módulo a tu archivo de Verse para usar definiciones de código de otros archivos de Verse.
using: para poder usar el contenido de un módulo de Verse, debes especificar el módulo por su ruta. | |
módulo: fuera de los módulos que las carpetas introducen a un proyecto, los módulos se pueden introducir dentro de un archivo .verse mediante la palabra clave |
Para más información, consulta Módulos y rutas.