usar recomendaciones personal para originales hashtags español ejemplos efectivo crear como c++ math programming-languages syntax language-design

c++ - recomendaciones - hashtag para instagram 2018



Sugerencias sobre la sintaxis para expresar de forma concisa la fórmula matemática (13)

Estoy desarrollando un lenguaje embebido específico de dominio funcional dentro de C ++ para traducir las fórmulas al código de trabajo de la manera más concisa y precisa posible.

Publiqué un prototipo en los comentarios, tiene unas doscientas líneas.

En este momento mi lenguaje se ve así (bueno, en realidad se verá así):

// implies two nested loops j=0:N, i=0,j (range(i) < j < N)[T(i,j) = (T(i,j) - T(j,i))/e(i+j)]; // implies summation over above expression sum(range(i) < j < N))[(T(i,j) - T(j,i))/e(i+j)];

Estoy buscando posibles mejoras / extensiones de sintaxis o simplemente ideas diferentes sobre cómo expresar fórmulas matemáticas de la forma más clara y precisa posible (en cualquier idioma, no solo en C ++).

¿Puede darme algunos ejemplos de sintaxis relacionados con mi pregunta que se pueden lograr en el idioma de su elección que considere útil? En particular, si tiene algunas ideas sobre cómo traducir los segmentos de código anteriores, me gustaría escucharlos.

Gracias.

Solo para aclarar y dar una fórmula real, mi objetivo a corto plazo es expresar lo siguiente

expresión concisamente donde los valores en <> ya están computados como matrices de 4 dimensiones.


¡Encapsular!

Estoy seguro de que encontrarás una sintaxis que equilibre la concisión y la claridad. Por muy bueno que sea, se mejorará enormemente proporcionando encapsulación. Entonces, en lugar de

qty = sum(range(i) < j < N))[(T(i,j) - T(j,i))/e(i+j)];

Podrías tener

Texpr = (T(i,j) - T(j,i))/e(i+j); RangeExpr = (range(i) < j < N); qty = sum(RangeExpr)[ Texpr ];

Eso podría permitirle más verbosidad en la sintaxis.

PD: ¿No es esto Mathematica? Interfaz de lenguaje Mathematica C / C ++



Daría una buena lectura al blog Project Fortress , tiene algunos mensajes inspiradores sobre notaciones matemáticas concisas para un lenguaje de programación.



Es posible que desee ver los idiomas APLish en busca de inspiración. Al trabajar con la matriz / matriz como un todo, puede reducir un poco la brevedad. Voy a usar Q a continuación, y a menos que malinterprete tu publicación, tu segunda declaración se puede escribir como:

sum raze (T-flip T) div e

Para entender T minus flip T, necesita mirar un ejemplo. Imagine que tenemos T configurado en la siguiente matriz:

0 1 2 3 4 5 6 7 8

flip T intercambia las dos dimensiones superiores, lo que da como resultado:

0 3 6 1 4 7 2 5 8

Así que T-flip T es básicamente T(i,j)-T(j,i) pero mucho menos tipeo, y por lo tanto, mucho menos propenso a errores.

El raze reduce una matriz a una sola dimensión, por lo que arrasa con esto:

0 3 6 1 4 7 2 5 8

lo convierte en esto:

0 3 6 1 4 7 2 5 8

Finalmente, la sum es sumatoria; básicamente suma todos los miembros de su lista, por lo que

sum 0 3 6 1 4 7 2 5 8

medio:

0+3+6+1+4+7+2+5+8

Implementar este tipo de cosas en C ++ no es tan difícil; tú podrías:

sum(raze((T-flip(T))/e))

con el nivel correcto de sobrecarga del operador.

Por cierto, K es aún más breve:

+/,/(T-+:T)%e


No estoy familiarizado con Phoenix y solo puedo hacer suposiciones sobre sus capacidades.

Siempre me ha gustado la forma en que Haskell me permite expresar rangos como una lista de comprensión. Se traduce muy bien de la notación matemática real en la construcción del lenguaje de programación.

[ i + j | i <- [1..10], j <- [1..10] ]

terminaría como algo así como:

[ i + j | i = range(1,10), j = range(1,10) ]

Lamentablemente, realmente no sé si algo así es posible con Phoenix.


No estoy seguro de cuán complicadas serán esas fórmulas, pero si llego al punto en que su API se parece más a ese dominio matemático que el estándar C ++ (que usa sobrecarga de operador y metaprogramación de plantillas hechas con bastante facilidad), creo que debería considerar desarrollar un lenguaje específico del dominio (DSL). Cuando intentas hacerlo en un idioma (como en tu caso) se llama DSL interna y, aunque tiene algunas ventajas, tiene muchas desventajas. Debe conocer mejor sus requisitos, sin embargo, le sugiero que busque herramientas para DSL externas, que son pequeños lenguajes externos especializados para cierto dominio. Mire en Jetbrains MPS y Eclipse Xtext, esas son dos herramientas de código abierto, que pueden usarse para un rápido desarrollo externo de DSL.


No me gusta la forma en que especifica ese rango 2d "triangular". Me gustaría ver algo como:

(i,j) in range(0..j,0..N)

por ejemplo, lo que podría conducir a:

X = sum(f(i,j) for (i,j) in range(0..j,0..N));

AFAIK Este no es un lenguaje existente, pero ya que estás creando tu propia sintaxis ... dudo de la posibilidad de usar j en la expresión de rango para i, pero encontraste la forma en la tuya :-)


Si busca la simplicidad, debe tomar la implícita de los bucles aún más. Por ejemplo, algo como esto

T( i < j , j < N ) = ( T(i,j) - T(j,i) )/e(i+j)

funcionará si reescribe el operador de asignación = para comportarse normalmente para algo como a(i) = b(i) + c(i) pero se comporta como una suma para a(i<5) = b(i) + c(i) Supongamos que la suma comienza desde 0 a menos que se especifique el límite inferior, por ejemplo, a(3<i<5) , compruebe si hay límites simbólicos superior / inferior que aparecen como índices de suma y si es necesario sume dos veces. Si quiere que la sintaxis fuerce la explicitud, podría definir un operador de suma separado s=

T( i < j , j < N ) s= ( T(i,j) - T(j,i) )/e(i+j)

No creo que puedas obtener un producto más limpio que este y aún así tener alguna utilidad de uso general. En cuanto a su objetivo a corto plazo, utilizando la noción de especificar el índice de suma al mismo tiempo que aparece el índice, puede escribir.

E_MP2 s= EV( i < n1 , j < n2 , a < n3 , b < n4 ) * 2 ( EV(a,b,i,j) - EV(a,b,j,i) ) / ( e(i)+e(j)-e(a)-e(b) )

donde explícitamente declaras que esto es una suma (usando s= ) haciendo que ese operador tome los índices de suma y los valores límite de la primera instancia que aparece un índice. Específicamente, también podría usar una sintaxis como (asumiendo ahora a, b fijo ei, j según su ejemplo)

E_MP2 s=(i<j,j<N) EV(i,j,a,b) * 2 ( EV(a,b,i,j) - EV(a,b,j,i) ) / ( e(i)+e(j)-e(a)-e(b) )

que es bastante claro notoriamente.

Luego podría continuar y llevar este concepto aún más allá, por ejemplo, definiendo un operador de integración i= que haga lo mismo. Es decir, busca instancias de variables marcadas con límites y luego integra la expresión con respecto a esas variables

F i=(0<x<Pi) x^-1 * exp(-I x^2)

De manera similar a la suma, puede especificar el límite cuando x ocurre por primera vez

F i= x[0,Pi]^-1 * exp(-I x^2)

donde los corchetes sirven para diferenciar la notación de la suma, para que no tenga que usar i= o s= y pueda usar tanto la suma como la integración al mismo tiempo:

F(i) = G(i,j<10) * x[0,inf]^-1 * H(i,j,x)


Si va a escribir esto para el mundo de ab-initio (que supongo que a partir de su ecuación MP2), quiere que sea muy fácil y claro expresar cosas tan cerca de la definición matemática que pueda.

Por un lado, no tendría la función de range complicado. Haga que defina un bucle, pero si desea bucles anidados, especifíquelos:

Entonces, en lugar de

(range(i) < j < N)[T(i,j) = (T(i,j) - T(j,i))/e(i+j)];

utilizar

loop(j,0,N)[loop(i,0,j)[T(i,j) = (T(i,j) - T(j,i))/e(i+j)]]

Y para cosas como suma y producto, haga que la sintaxis "herede" del hecho de que es un ciclo.

Entonces, en lugar de

sum(range(i) < j < N))[(T(i,j) - T(j,i))/e(i+j)];

utilizar

sum(j,0,n)[loop(i,0,j)[(T(i,j) - T(j,i))/e(i+j)]]

o si necesitas una suma doble

sum(j,0,n)[sum(i,0,j)[(T(i,j) - T(j,i))/e(i+j)]]

Dado que parece que intentas representar operadores de mecánica cuántica, intenta hacer que tus constructos de lenguaje coincidan lo más posible con el operador en una base de 1-1. De esta manera, es fácil de traducir (y claro sobre lo que se está traduciendo).

EDITADO PARA AGREGAR

ya que estás haciendo química cuántica, entonces es bastante fácil (al menos como lo hace la sintaxis). Usted define operadores que siempre trabajan en lo que está a su derecha y, a continuación, lo único que necesita son paréntesis para agrupar donde se detiene un operador.

La notación Einstein es divertida, donde no se especifican los índices o límites y están implícitos por convención, sin embargo, eso no hace que el código sea claro y es más difícil pensar en ello.

Para las sumas, incluso si los límites implicados, siempre son fáciles de entender en función del contexto, por lo que siempre debe hacer que las personas los especifiquen.

suma (i, 0, n) suma (j, 0, i) suma (a, -j, j) suma (b, -i, i) ....

Como cada operador trabaja a la derecha, sus variables son conocidas, por lo que j puede saber acerca de i, puede saber acerca de i, j y b puede saber acerca de i, j y a.

De mi experiencia con químicos cuánticos (¡yo también soy uno!) No les gusta la sintaxis complicada que difiere mucho de lo que escriben. Están contentos de separar sumas y integrales dobles y triples en una colección de singles porque de todas maneras son taquigrafía.

La simetría tampoco va a ser tan difícil. Es solo una colección de swaps y agrega o multiplica. Haría algo donde especifiques la operación que contiene una lista de los elementos que son iguales y se pueden intercambiar:

c2v (sigma_x, a, b) a + b

Esto dice que a y b pueden considerarse partículas idénticas en una operación c2v. Eso significa que cualquier ecuación con a y b (como a + b después de ella) se debe transformar en una combinación lineal de las transformaciones c2v. sigma_x es la operación en c2v que desea aplicar a su función, (a + b). Si mal no recuerdo, eso es 1 / sqrt (2) ((a + b) + (b + a)). Pero no tengo mi libro de simetría aquí, así que podría estar mal.


Yo preferiría una separación más sólida entre los bucles. Por ejemplo, preferiría esta notación alternativa a su segundo ejemplo:

sum(range(j) < N)[sum(range(i) < j)[(T(i,j) - T(j,i))/e(i+j)]]

También encuentro la sintaxis de rango difícil. Creo que un rango debe ser un componente único. Prefiero algo así:

j = range_iter(0,N)

Eso se abriría para el range x(0,N); j = range.begin(); range x(0,N); j = range.begin(); o alternativas que no puedo pensar ahora.

Usted puede incluso:

j = range_iter(inc(0) => exc(N)); para j itera sobre [0, N).

De todos modos, una idea interesante. ¿Pueden ser compuestas sus funciones resultantes? ¿Puedes solicitar información de dominio?


mi prototipo (todavía necesita mucho trabajo, obviamente, y comentarios)

// #include "tensor/tensor.hpp" // #include "typename.hpp" #include <boost/spirit/home/phoenix/core/argument.hpp> #include <boost/spirit/include/phoenix_core.hpp> #include <boost/spirit/include/phoenix_operator.hpp> #define BOOST_FUSION_INVOKE_FUNCTION_OBJECT_MAX_ARITY PHOENIX_ARG_LIMIT #include <boost/fusion/functional/invocation/limits.hpp> #include <boost/fusion/functional.hpp> #include <boost/fusion/include/intrinsic.hpp> #include <boost/fusion/include/vector.hpp> #include <boost/fusion/include/algorithm.hpp> #include <boost/fusion/include/vector_tie.hpp> #include <boost/fusion/include/make_vector.hpp> #include <boost/typeof/typeof.hpp> namespace range_ { namespace detail { namespace phoenix = boost::phoenix; namespace fusion = boost::fusion; /// undefined or implicit object struct undefined {}; template<int N> struct index { // index(boost::phoenix::argument<N>) {} typedef phoenix::actor<phoenix::argument<N> > argument; argument arg() const { return argument(); } }; template<typename T, typename U = void> struct functional { typedef phoenix::actor<phoenix::value<T> > type; static type form(const T& t) { return type(t); } }; template<typename T, typename U> struct functional<phoenix::actor<T>, U> { typedef phoenix::actor<T> type; static type form(const T& t) { return type(t); } }; template<typename T> struct functional<undefined,T> { typedef typename functional<T>::type type; static type form(const undefined&) { return type(T()); } }; template<int N, class L, class U, class T = undefined> struct expression; template<int N, class L, class U, class C> struct expression { typedef functional<L,U> lower_function; typedef functional<U,L> upper_function; expression(const L &lower, const U& upper, const C &cdr) : lower_(lower), upper_(upper), cdr_(cdr) {} template<class E> expression<N, L, U, E> operator()(const E &c) const { return expression<N, L, U, E>(lower_, upper_, c); } template<class F> void operator[](const F &f) const { #define TEXT(z, n, text) text #define UNDEFINED_ARGUMENTS BOOST_PP_ENUM(PHOENIX_ARG_LIMIT, TEXT, undefined()) evaluate<int>(f, fusion::make_vector(UNDEFINED_ARGUMENTS)); #undef TEXT #undef UNDEFINED_ARGUMENTS } L lower_; U upper_; C cdr_; const L& left() const { return lower_; } const C& cdr() const {return cdr_; } template<typename T> typename functional<L,T>::type begin() const { return functional<L,T>::form(lower_); } template<typename T> typename functional<U,T>::type end() const { return functional<U,T>::form(upper_); } template<typename T, class F, class A> void evaluate(const F &f, const A &arguments) const { T i = this->begin<T>()(arguments); #define AS_VECTOR(var, expr) BOOST_AUTO(var, fusion::as_vector(expr)) #define ADVANCE_C(seq) fusion::advance_c<N>(fusion::begin(seq)) AS_VECTOR(b, fusion::erase(arguments, ADVANCE_C(arguments))); AS_VECTOR(a, fusion::insert_range(b, ADVANCE_C(b), fusion::vector_tie(i))); #undef ADVANCE_C #undef AS_VECTOR while (fusion::invoke_function_object(this->end<T>(), a)) { this->apply<T>(cdr_, f, a); ++i; } } template<typename T, class E, class F, class V> void apply(const E &e, const F &f, const V &variables) const { e.template evaluate<T>(f, variables); } template<typename T, class F, class V> void apply(const undefined&, const F &f, const V &variables) const { fusion::invoke_function_object(f, fusion::as_vector(variables)); } }; template<int N, class L, class U> expression<N, L, U> make_expression(const L &lower, const U& upper) { return expression<N, L, U>(lower, upper, undefined()); } template<int N, class L, class U> expression<N, L, U> make_expression(const expression<N, L, undefined> &expr, const U& right) { return expression<N, L, U>(expr.left(), right, undefined()); } template<int N1, class L1, class U1, class T1, int N2, class L2, class U2> expression<N2, L2, U2, expression<N1, L1, U1, T1> > operator,(const expression<N1, L1, U1, T1> &e1, const expression<N2, L2, U2> &e2) { return e2(e1); } #define ARGUMENT(N) phoenix::actor<phoenix::argument<N> > #define ACTOR_COMPOSITE(O,L,R) / phoenix::actor<typename phoenix::as_composite<O, L, R>::type> #define LOWER_BOUND_OPERATOR(op, eval, param) / template <typename T, int N> / expression<N, param, undefined> / operator op (const param& left, const index<N> &i) { / return make_expression<N>(left, undefined()); / } #define UPPER_BOUND_OPERATOR_INDEX(op, eval, param) / template <typename T, int N> / expression<N, undefined, / ACTOR_COMPOSITE(eval, ARGUMENT(N), param)> / operator op (const index<N> &i, const param& e) { / return make_expression<N>(undefined(), / (ARGUMENT(N)() op e)); / } #define UPPER_BOUND_OPERATOR_EXPRESSION(op, eval) / template <typename T, int N, class E> / expression<N, E, ACTOR_COMPOSITE(eval, ARGUMENT(N), T)> / operator op (const expression<N, E, undefined> &left, / const T& right) { / return make_expression(left, (ARGUMENT(N)() op right)); / } #define UPPER_BOUND_OPERATOR(op, eval) / UPPER_BOUND_OPERATOR_INDEX(op, eval, T) / UPPER_BOUND_OPERATOR_INDEX(op, eval, phoenix::actor<T>) / UPPER_BOUND_OPERATOR_EXPRESSION(op, eval) LOWER_BOUND_OPERATOR( < , phoenix::less_eval, T) LOWER_BOUND_OPERATOR( < , phoenix::less_eval, phoenix::actor<T>) LOWER_BOUND_OPERATOR( <= , phoenix::less_equal_eval, T) LOWER_BOUND_OPERATOR( <= , phoenix::less_equal_eval, phoenix::actor<T>) UPPER_BOUND_OPERATOR( < , phoenix::less_eval) UPPER_BOUND_OPERATOR( <= , phoenix::less_equal_eval) } } namespace index { using namespace boost::phoenix; boost::phoenix::actor<boost::phoenix::argument<0> > const i; boost::phoenix::actor<boost::phoenix::argument<1> > const j; boost::phoenix::actor<boost::phoenix::argument<2> > const k; boost::phoenix::actor<boost::phoenix::argument<3> > const l; template<int N> range_::detail::index<N> range(const boost::phoenix::actor< boost::phoenix::argument<N> >&) { return range_::detail::index<N>(); } template<int N, class F> range_::detail::index<N> range(const boost::phoenix::actor< boost::phoenix::argument<N> >&, const F &f) { // return range_::detail::index<N>(); throw std::exception("not implemented"); } } int main(){ using namespace index; // formula domain language rough prototype // stuff in brackets can be any valid phoenix lambda expression // physicist notation, lower bound may be implicit (range(i) <= j, 3 <= range(j) < 4)[std::cout << i << " " << j << std::endl]; // implicit physicist notation, not done yet //(range(i) < range(j) < 4)[...] // programmer notation, same as above , but order is reversed (3 <= range(j) < 4)(range(i) <= j)[std::cout << i << " " << j << std::endl]; // ignore, some early prototype for lambda tensor // size_t N = 4; // tensor::tensor<4> T(N,N,N,N); // tensor::function<tensor::tensor<4> > T_(T); // (range(j) < 4)(range(i) <= j)[std::cout << T_(i,j,0,0)]; // (range(i) < j, range(j) < N)[T_(i,j,0,0) = T_(j,i,0,0)]; // sum(j < val(N))[T_(i,j,0,0)]; }


T = T - T''/e;

o, si no está operando en todo T

T(0:i,0:j) = T(0:i,0:j) - T(0:i,0:j)''/e(0:i,0:j)