Обратите внимание, что (.|\n)*
может быть менее эффективным, чем (например) [\s\S]*
(если регулярные выражения вашего языка поддерживают такие escape-последовательности), а не поиск того, как указать модификатор, который делает. также соответствуют новостям. Или вы можете пойти с альтернативами POSIXy, такими как [[:space:][:^space:]]*
.
Если вы собираетесь писать это для мира ab-initio (что я предполагаю из вашего уравнения MP2), вам нужно сделать очень простым и понятным выражение вещей, максимально приближенных к математическому определению. что ты можешь.
Во-первых, у меня не было бы сложной функции range
. Определите цикл, но если вам нужны вложенные циклы, укажите их оба:
Итак, вместо
(range (i)
используйте
loop (j, 0, N) [loop (i, 0, j) [T (i, j) = (T (i, j) - T (j, i)) / e (i + j)]]
А для таких вещей, как сумма и произведение, сделайте синтаксис «унаследованным» от того факта, что это цикл.
Итак, вместо
sum (range (i)
используйте
sum (j, 0, n) [loop (i, 0, j) [(T (i, j) - T (j, i)) / e (i + j)]]
или если вам нужна двойная сумма
sum (j, 0, n) [sum (i, 0, j) [(T (i, j) - T (j, i)) / e (i + j)]]
Поскольку похоже, что вы пытаетесь представить квантово-механические операторы, постарайтесь сделать так, чтобы ваши языковые конструкции соответствовали оператору как можно точнее. Таким образом, будет легко переводить (и ясно понимать, что переводится).
РЕДАКТИРОВАТЬ ДОБАВИТЬ
, поскольку вы занимаетесь квантовой химией, это довольно просто (по крайней мере, с точки зрения синтаксиса). Вы определяете операторы, которые всегда работают с тем, что находится справа от них, а затем единственное, что вам нужно, - это скобки для группировки в месте остановки оператора.
Нотация Эйнштейна - это развлечение, когда вы не указываете индексы или границы, и они подразумеваются из-за соглашения, однако это не делает четкий код, и о нем труднее думать.
Для сумм, даже если подразумеваются границы, их всегда легко вычислить на основе контекста, поэтому вы всегда должны заставлять людей указывать их.
sum (i, 0, n) sum (j, 0, i) sum (a, -j, j) sum (b, -i, i) ....
Поскольку каждый оператор работает с верно, его переменные известны, поэтому j может знать о i, a может знать о i и j, а b может знать о i, j и a.
Судя по моему опыту работы с квантовыми химиками (я тоже!), Им не нравится сложный синтаксис, который сильно отличается от того, что они пишут. Они с удовольствием разделяют двойные и тройные суммы и интегралы на набор одиночных чисел, потому что это в любом случае просто сокращение.
С симметрией тоже не так уж сложно. Это просто набор перестановок и сложений или умножений. Я бы сделал что-нибудь, где вы укажете операцию, которая содержит список одинаковых элементов, которые можно поменять местами:
c2v (sigma_x, a, b) a + b
Здесь говорится, что a и b являются можно рассматривать как идентичные частицы при операции c2v. Это означает, что любое уравнение с a и b (например, a + b после него) должно быть преобразовано в линейную комбинацию преобразований c2v. sigma_x - это операция в c2v, которую вы хотите применить к своей функции (a + b). Если я правильно помню, это 1 / sqrt (2) ((a + b) + (b + a)). Но у меня здесь нет книги по симметрии, так что это может быть неправильно.
Мне не нравится, как вы указываете этот "треугольный" 2-мерный диапазон. Я бы хотел увидеть что-то вроде:
(i,j) in range(0..j,0..N)
например, что могло бы затем привести к:
X = sum(f(i,j) for (i,j) in range(0..j,0..N));
AFAIK Это не существующий язык, но поскольку вы создаете свой собственный синтаксис ... Я сомневаюсь в том, что возможность использовать j в выражении диапазона для i, но вы нашли способ в своем: -)
T = T - T'/e;
или, если вы работаете не на всех T
T(0:i,0:j) = T(0:i,0:j) - T(0:i,0:j)'/e(0:i,0:j)
Я бы предпочел более плотное разделение между петлями. Например, я бы предпочел эту альтернативную запись вашему второму примеру:
sum (range (j)
Я также нахожу сложным синтаксис диапазона. Я считаю, что линейка должна быть единой составляющей. Я бы предпочел что-то вроде этого:
j = range_iter (0, N)
Это открылось бы для range x (0, N); j = range.begin ();
или альтернативы, которые я не могу придумать прямо сейчас.
Вы можете даже:
j = range_iter (inc (0) => exc (N));
for j выполняет итерацию по [0, N).
Во всяком случае, интересная идея. Можно ли составить ваши результирующие функции? Вы можете запросить информацию о домене?
Я бы подумал, что выражает математические формулы так, как они делаются в LaTeX . В конце концов, LaTeX уже четко определен и хорошо задокументирован в этой области.
Я не знаком с Phoenix и могу только строить предположения о его возможностях.
Мне всегда нравилось, как Хаскель позволяет выражать диапазоны в виде списков. Это хорошо переводит из реальной математической нотации в конструкцию языка программирования.
[ i + j | i <- [1..10], j <- [1..10] ]
в итоге получится что-то вроде:
[ i + j | i = range(1,10), j = range(1,10) ]
К сожалению, я не знаю, возможно ли что-то подобное в Phoenix.
Я не уверен, насколько сложными будут эти формулы, но если дойти до того момента, когда ваш API больше похож на этот математический домен, чем на стандартный C ++ (который довольно легко использует перегрузку операторов и метапрограммирование шаблонов), я бы предположил, что вы следует рассмотреть возможность разработки предметно-ориентированного языка (DSL). Когда вы пытаетесь сделать это на языке (например, в вашем случае), это называется внутренним DSL, и, хотя у него есть некоторые преимущества, у него есть много недостатков. Вы должны лучше знать свои требования, однако я хочу предложить вам изучить инструменты для внешних DSL, которые представляют собой небольшие внешние языки, специализированные для определенной области. Посмотрите на Jetbrains MPS и Eclipse Xtext, это два инструмента с открытым исходным кодом, которые можно использовать для быстрой разработки внешних DSL.
мой прототип (все еще требует много работы, очевидно, и комментариев)
// #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)];
}
Encapsulate!
Я уверен, что вы найдете синтаксис, который уравновешивает краткость и ясность. Как бы хорошо это ни было, он будет значительно улучшен, обеспечив инкапсуляцию. Таким образом, вместо
qty = sum(range(i) < j < N))[(T(i,j) - T(j,i))/e(i+j)];
Вы могли бы иметь
Texpr = (T(i,j) - T(j,i))/e(i+j);
RangeExpr = (range(i) < j < N);
qty = sum(RangeExpr)[ Texpr ];
Это может быть предоставлено вам больше многословия в синтаксисе.
PS: Разве это не Mathematica? Интерфейс языка Mathematica C/C++
Если вам нужна простота, вам следует пойти дальше неявности циклов. Например, что-то вроде этого
T( i < j , j < N ) = ( T(i,j) - T(j,i) )/e(i+j)
будет работать, если вы переписываете оператор присваивания =
, чтобы он вел себя нормально для чего-то вроде a (i) = b (i) + c (i)
, но вести себя как суммирование для a (i <5) = b (i) + c (i)
. Предположим, что суммирование начинается с 0, если не указан нижний предел, например a (3 , проверьте символические верхние / нижние пределы, которые появляются как индексы суммирования, и при необходимости сделайте двойные суммы. Если вы хотите, чтобы синтаксис был явным, вы могли бы определить отдельный оператор суммы
s =
T( i < j , j < N ) s= ( T(i,j) - T(j,i) )/e(i+j)
Я не думаю, что вы можете получить что-то более чистое, чем это, и при этом иметь некоторую универсальность использования. Что касается вашей краткосрочной цели, вы можете написать, используя понятие указания индекса суммирования одновременно с первым появлением индекса.
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) )
, где вы явно указываете, что это сумма (используя s =
), заставляя этот оператор затем брать индексы суммирования и ограничивающие значения из первого экземпляра индекса. В частности, вы также можете использовать синтаксис вроде (при условии, что теперь a, b фиксированы и i, j в соответствии с вашим примером)
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) )
, который довольно ясен с точки зрения обозначений.
Затем вы могли бы продолжить и развить эту концепцию еще дальше, например, определив оператор интегрирования i =
, который делает то же самое. Т.е. он ищет экземпляры переменных, которые отмечены ограничениями, а затем переходит к интегрированию выражения относительно этих переменных
F i=(0<x<Pi) x^-1 * exp(-I x^2)
аналогично суммированию, вы можете указать предел, когда x впервые встречается
F i= x[0,Pi]^-1 * exp(-I x^2)
, где квадратные скобки служат для дифференциации обозначение из суммирования, поэтому вам не нужно использовать i =
или s =
и может использовать как суммирование, так и интегрирование одновременно:
F(i) = G(i,j<10) * x[0,inf]^-1 * H(i,j,x)
Вы можете посмотреть на APLish-языки для вдохновения. Работая с матрицей / массивом в целом, вы можете немного снизить краткость. Я собираюсь использовать Q ниже, и если я неправильно прочитал ваш пост, ваше второе утверждение может быть записано как:
sum raze (T-flip T) div e
Чтобы понять T минус переворот T, вам нужно взглянуть на пример.Представьте, что мы установили T в следующую матрицу:
0 1 2
3 4 5
6 7 8
flip T меняет местами два верхних измерения, что приводит к:
0 3 6
1 4 7
2 5 8
Итак T-flip T
в основном равен T (i, j) -T (j, i)
, но намного меньше набирать и, следовательно, меньше подвержено ошибкам.
raze
уменьшает массив до единственного измерения, поэтому уничтожение этого:
0 3 6
1 4 7
2 5 8
превращает его в следующее:
0 3 6 1 4 7 2 5 8
Наконец, сумма
суммируется; он в основном складывает все элементы своего списка, поэтому
sum 0 3 6 1 4 7 2 5 8
означает:
0+3+6+1+4+7+2+5+8
Реализовать такого рода вещи в C ++ не так уж и сложно; вы можете:
sum(raze((T-flip(T))/e))
с правильным уровнем перегрузки оператора.
Кстати, K еще короче:
+/,/(T-+:T)%e
Я бы хорошо почитал блог Project Fortress, там есть несколько вдохновляющих постов о математически кратких обозначениях для языка программирования.
Вам следует взглянуть на синтаксис, используемый для понимания списка Haskell .