En el siguiente código, estoy tratando de crear un "puntero de función" y una matriz de funciones considerando los nombres de las funciones como variables habituales:

proc myfunc1() { return 100; }
proc myfunc2() { return 200; }

// a function variable?
var myfunc = myfunc1;
writeln( myfunc() );

myfunc = myfunc2;
writeln( myfunc() );

// an array of functions?
var myfuncs: [1..2] myfunc1.type;

writeln( myfuncs.type: string );

myfuncs[ 1 ] = myfunc1;
myfuncs[ 2 ] = myfunc2;

for fun in myfuncs do
    writeln( fun() );

Que parece estar funcionando como se esperaba (con Chapel v1.16)

100
200
[domain(1,int(64),false)] chpl__fcf_type_void_int64_t
100
200

Entonces, me pregunto si el uso anterior de variables de función es legítimo. Para crear una matriz de funciones, ¿es habitual definir una función concreta con la firma deseada primero y luego referirse a su tipo (con .type) como en el ejemplo anterior?

Además, ¿no es un problema tratar estas variables como variables "habituales", por ejemplo, pasarlas a otras funciones como argumentos o incluirlas como un campo de clase / registro? (Por favor, ignore estas últimas preguntas si son demasiado amplias ...) Agradecería cualquier consejo si existen posibles dificultades (si las hubiera).

3
roygvib 21 feb. 2018 a las 05:55

2 respuestas

La mejor respuesta

Este código utiliza soporte de funciones de primera clase, que es un prototipo / borrador en el diseño del lenguaje Chapel. Puede leer más sobre el soporte del prototipo en las Funciones de primera clase en la capilla nota técnica.

Si bien muchos usos de funciones de primera clase funcionan en 1.16 y versiones posteriores, puede esperar que se revise el diseño del lenguaje en esta área. En particular, actualmente no existe una respuesta razonable a la pregunta de si se pueden capturar o no variables (y en este momento intentar hacerlo probablemente resulte en un error confuso). Sin embargo, no sé en qué versión futura cambiará esto.

Con respecto a la parte myfunc1.type, la sección de la nota técnica a la que me referí llamada "Especificar el tipo de función de primera clase" presenta una estrategia alternativa. Sin embargo, no veo ningún problema con el uso de myfunc1.type en este caso.

Por último, tenga en cuenta que el soporte de lambda en el compilador actual en realidad opera creando una clase con un método this. Entonces puede hacer lo mismo - crear un "objeto de función" (para tomar prestado un término de C ++) - que tenga el mismo efecto. Un "objeto de función" puede ser un registro o una clase. Si se trata de una clase, puede usar la herencia para poder crear una matriz de objetos que puedan responder al mismo método según su tipo dinámico. Esta estrategia podría permitirle solucionar problemas actuales con funciones de primera clase. Incluso si se completa el soporte de funciones de primera clase, el enfoque de "objeto de función" le permite ser más explícito sobre las variables capturadas. En particular, puede almacenarlos como campos en la clase y establecerlos en el inicializador de la clase. A continuación, se muestra un ejemplo de creación y uso de una matriz de diferentes tipos de objetos de función:

class BaseHandler {
  // consider these as "pure virtual" functions
  proc name():string { halt("base name called"); }
  proc this(arg:int) { halt("base greet called"); }
}
class HelloHandler : BaseHandler {
  proc name():string { return "hello"; }
  proc this(arg:int) { writeln("Hello ", arg); }
}
class CiaoHandler : BaseHandler {
  proc name():string { return "ciao"; }
  proc this(arg:int) { writeln("Ciao ", arg); }
}

proc test() {
  // create an array of handlers
  var handlers:[1..0] BaseHandler;
  handlers.push_back(new HelloHandler());
  handlers.push_back(new CiaoHandler());

  for h in handlers {
    h(1); // calls 'this' method in instance
  }
}

test();
3
mppf 21 feb. 2018 a las 14:15

Sí, en su ejemplo está utilizando el soporte inicial de Chapel para funciones de primera clase. Para su segunda pregunta, alternativamente podría usar una asistente de tipo de función para la declaración de la matriz de funciones:

var myfuncs: [1..2] func(int);

Estos objetos de función de primera clase se pueden pasar como argumentos a funciones; así es como { { }} X0 trabajos - o almacenados como campos de un registro (< a href = " https://tio.run/##dY6xDsIgEIZ3nuIfaWwcjEtLuvoKzkhp2gQpAWrbmD47niRVFxcu3P33fad66bRJSRkZAi6TVVcvndMeTwao0YaIztboaMIHG0vsTyEo4Pyo6DNE/idUZA4Q@yEcO4uGaCJ3wkSWY97NqI1tjD2kp8B@QgOr59@juJH3Wyv5UmfB@i5kgNdx8hYLDlgFNuLNfojaWP6BkZyfTyWqqqBxq42O @ qsSKb0A "rel =" nofollow noreferrer "> ¡Pruébelo en línea! ejemplo ). Las capacidades de funciones de primera clase de Chapel también incluyen funciones lambda.

Para ser claros, el aspecto "inicial" de este soporte viene con la advertencia (de la documentación):

Este mecanismo debe considerarse una tecnología provisional hasta que hayamos desarrollado e implementado una historia más sólida, por lo que se describe en este README en lugar de en la especificación del lenguaje.

2
Nick 21 feb. 2018 a las 14:16