Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Boost.Hana: Logical

Boost , ,

Boost.Hana  1.0.1
Your standard library for metaprogramming
Понятие Logical представляет типы с истинным значением.

Интуитивно Logical — это просто bool или что-то, что может действовать как один. Однако в контексте программирования с разнородными объектами становится чрезвычайно важным различать те объекты, истинное значение которых известно во время компиляции, и те, истинное значение которых известно только во время выполнения. Причина, по которой это так важно, заключается в том, что можно разветвляться во время компиляции на условии, истинное значение которого известно во время компиляции, и, следовательно, тип возврата заключающей функции может зависеть от этого истинного значения. Однако, если значение истинности известно только во время выполнения, компилятор должен компилировать обе ветви (потому что любая или обе из них могут в конечном итоге использоваться), что создает дополнительное требование, что обе ветви должны оцениваться к одному и тому же типу.

Более конкретно, Logical (почти) представляет собой булевую алгебру , которая представляет собой математическую структуру, кодирующую обычные свойства, которые позволяют нам рассуждать с bool. Точные свойства, которые должны быть удовлетворены любой моделью Logical, строго указаны в законах ниже.

Truth, falsity and logical equivalence

Logical x называется true-valued, а иногда и просто true как злоупотребление обозначением, если

if_(x, true, false) == true

Аналогично, x является фальсифицированным , или иногда просто фальсифицированным , если

if_(x, true, false) == false

Это обеспечивает стандартный способ преобразования любого Logical в прямой bool. Понятие истинной ценности предполагает другое определение, а именно логическую эквивалентность. Мы скажем, что два Logicals x и y являются логически эквивалентными, если они имеют одинаковое значение истины. Чтобы обозначить, что некоторые выражения p и q логического типа данных логически эквивалентны, мы иногда также пишем:

p if and only if q

это очень распространено в математике. Интуиция, стоящая за этой записью, заключается в том, что всякий раз, когда p является истинным значением, тогда q должен быть; но когда p является ложным значением, тогда q также должен быть. Следовательно, p должно быть истинно оценено, когда (и только когда) q истинно оценено.

Minimal complete definition

eval_if, not_ и while_

Все остальные функции можно определить в таких терминах:

if_(cond, x, y) = eval_if(cond, lazy(x), lazy(y))
and_(x, y) = if_(x, y, x)
or_(x, y) = if_(x, x, y)
etc...

Laws

Как описано выше, концепция Logical представляет собой булеву алгебру. Обоснование этой слабости состоит в том, чтобы позволить таким вещам, как целые числа, действовать как Логические , которые выровнены с C++, даже если они не образуют булеву алгебру. Несмотря на то, что мы отходим от обычной аксиоматизации булевых алгебр, мы на собственном опыте обнаружили, что данное здесь определение логики в значительной степени совместимо с интуицией.

Следующие законы должны быть выполнены для любого типа данных L, моделирующего концепцию Logical. Пусть a, b и c будут объектами типа данных Logical, и пусть t и f будут произвольными true-valued и false-valued Logicals этого типа данных соответственно. Тогда,

// associativity
or_(a, or_(b, c)) == or_(or_(a, b), c)
and_(a, and_(b, c)) == and_(and_(a, b), c)
// equivalence through commutativity
or_(a, b) if and only if or_(b, a)
and_(a, b) if and only if and_(b, a)
// absorption
or_(a, and_(a, b)) == a
and_(a, or_(a, b)) == a
// left identity
or_(a, f) == a
and_(a, t) == a
// distributivity
or_(a, and_(b, c)) == and_(or_(a, b), or_(a, c))
and_(a, or_(b, c)) == or_(and_(a, b), and_(a, c))
// complements
or_(a, not_(a)) is true-valued
and_(a, not_(a)) is false-valued

Why is the above not a boolean algebra?

Если вы посмотрите внимательно, вы обнаружите, что мы отходим от обычных булевых алгебр, потому что:

  1. мы не требуем, чтобы элементы, представляющие истину и ложь, были уникальными.
  2. мы не применяем коммутативность операцийand_иor_.
  3. поскольку мы не применяем коммутативность, законы идентичности становятся законами левой идентичности

Concrete models

hana::integral_constant

Free model for arithmetic data types

Тип данных T является арифметическим, если std::is_arithmetic::value является истинным. Для арифметического типа данных T модель Logical обеспечивается автоматически путем использования результата встроенного неявного преобразования в bool в качестве значения истинности. В частности, минимальное полное определение для этих типов данных

eval_if(cond, then, else_) = cond ? then(id) : else(id)
not_(cond) = static_cast<T>(cond ? false : true)
while_(pred, state, f) = equivalent to a normal while loop

Rationale for not providing a model for all contextually convertible to bool data types

Метод not_ не может быть реализован для всех этих типов. Например, нельзя отбрасывать тип указателя T*, а затем снова возвращаться к T*. С арифметическим типом T, однако, можно отлить от T до кипения, а затем снова до T; результат будет 0 или 1 в зависимости от значения истинности. Если вы хотите использовать тип указателя или что-то подобное в условном режиме, рекомендуется явно преобразовать его в bool с помощью to.

Variables

constexpr auto boost::hana::and_
 Return whether all the arguments are true-valued.and_ can be called with one argument or more. When called with two arguments, and_ uses tag-dispatching to find the right implementation. Otherwise,. More...
 
constexpr auto boost::hana::eval_if
 Conditionally execute one of two branches based on a condition.Given a condition and two branches in the form of lambdas or hana::lazys, eval_if will evaluate the branch selected by the condition with eval and return the result. The exact requirements for what the branches may be are the same requirements as those for the eval function. More...
 
constexpr auto boost::hana::if_
 Conditionally return one of two values based on a condition.Specifically, then is returned iff cond is true-valued, and else_ is returned otherwise. Note that some Logical models may allow then and else_ to have different types, while others may require both values to have the same type. More...
 
constexpr auto boost::hana::not_
 Negates a Logical.This method returns a Logical with the same tag, but whose truth-value is negated. Specifically, not_(x) returns a false-valued Logical if x is a true-valued Logical, and a true-valued one otherwise. More...
 
constexpr auto boost::hana::or_
 Return whether any of the arguments is true-valued.or_ can be called with one argument or more. When called with two arguments, or_ uses tag-dispatching to find the right implementation. Otherwise,. More...
 
constexpr auto boost::hana::while_
 Apply a function to an initial state while some predicate is satisfied.This method is a natural extension of the while language construct to manipulate a state whose type may change from one iteration to another. However, note that having a state whose type changes from one iteration to the other is only possible as long as the predicate returns a Logical whose truth value is known at compile-time. More...
 

Variable Documentation

constexpr auto boost::hana::and_

#include <boost/hana/fwd/and.hpp>

Initial value:
= [](auto&& x, auto&& ...y) -> decltype(auto) {
return tag-dispatched;
}

Возвращаем, являются ли все аргументы истинными. и_ можно назвать одним аргументом или более. При вызове с двумя аргументами и_ для поиска правильной реализации используется диспетчеризация тегов. Иначе.

and_(x) == x
and_(x, y, ...z) == and_(and_(x, y), z...)

Example

// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
namespace hana = boost::hana;
BOOST_HANA_CONSTANT_CHECK(hana::and_(hana::true_c, hana::true_c, hana::true_c, hana::true_c));
static_assert(!hana::and_(hana::true_c, false, hana::true_c, hana::true_c), "");
int main() { }
constexpr auto boost::hana::eval_if

#include <boost/hana/fwd/eval_if.hpp>

Initial value:
= [](auto&& cond, auto&& then, auto&& else_) -> decltype(auto) {
return tag-dispatched;
}
constexpr auto then
Sequentially compose two monadic actions, discarding any value produced by the first but not its effe...
Definition: then.hpp:36

Условно выполнить одну из двух ветвей на основании условия. При условии и двух ветвях в виде лямбда или hana::lazys, eval_if будет оценивать ветвь, выбранную условием с eval и возвращать результат. Точные требования к ветвям те же, что и для функции eval.

Deferring compile-time evaluation inside eval_if

Пройдя унарный вызов eval_if, можно отложить оценку выбранных выражений внутри лямбды. Это полезно, когда инстанцирование ветви вызовет ошибку времени компиляции; мы хотим, чтобы ветвь была инстанциирована только тогда, когда эта ветвь выбрана. Вот как этого можно достичь.

Для простоты мы будем использовать унарную лямбду в качестве нашего унарного вызова. Наша лямбда должна принимать параметр (обычно называемый _), который может использоваться для отсрочки оценки выражений по мере необходимости. Например,

template <typename N>
auto fact(N n) {
return hana::eval_if(n == hana::int_c<0>,
[] { return hana::int_c<1>; },
[=](auto _) { return n * fact(_(n) - hana::int_c<1>); }
);
}

Здесь происходит то, что eval_if вызовет eval в выбранной ветви. В свою очередь, eval будет называть выбранную ветвь либо ничем; для тогда ветвь – или с hana::id – для else ветвь. Поэтому, _(x) всегда совпадает с x, но компилятор не может сказать, пока лямбда не будет вызвана! Следовательно, компилятор должен ждать, прежде чем он инстанцирует тело лямбды, и не происходит бесконечной рекурсии. Однако этот трюк для задержки инстанциации тела лямбды может быть использован только тогда, когда состояние известно во время компиляции, потому что в противном случае обе ветви должны быть инстанцированы внутри eval_if.

Есть несколько предостережений, которые следует отметить при таком подходе к ленивому ветвлению. Во-первых, поскольку мы используем лямбда, это означает, что результат функции не может быть использован в постоянном выражении. Это ограничение нынешнего языка.

Вторая оговорка заключается в том, что у компиляторов в настоящее время есть несколько ошибок в отношении глубоко вложенных лямбд с захватами. Таким образом, вы всегда рискуете разбить компилятор, но это вопрос времени, прежде чем это больше не проблема.

Наконец, это означает, что условия не могут быть написаны непосредственно в неоцененных контекстах. Причина в том, что лямбда не может появиться в неоцененном контексте, например, в деклатипе . Один из способов обойти это — полностью перевести вычисления вашего типа в переменные шаблоны. Вместо того, чтобы писать

template <typename T>
struct pointerize : decltype(
hana::eval_if(hana::traits::is_pointer(hana::type_c<T>),
[] { return hana::type_c<T>; },
[](auto _) { return _(hana::traits::add_pointer)(hana::type_c<T>); }
))
{ };

вы можете вместо этого написать

template <typename T>
auto pointerize_impl(T t) {
return hana::eval_if(hana::traits::is_pointer(t),
[] { return hana::type_c<T>; },
[](auto _) { return _(hana::traits::add_pointer)(hana::type_c<T>); }
);
}
template <typename T>
using pointerize = decltype(pointerize_impl(hana::type_c<T>));

Примечание : Этот пример на самом деле будет реализован легче с частичными специализациями, но мой пакет хороших примеров пуст на момент написания этого.

Теперь этот прыжок с обруча должен быть сделан только в одном месте, потому что вы должны использовать нормальную нотацию функций везде в вашей метапрограмме для выполнения вычислений типов. Таким образом, синтаксическая стоимость амортизируется по всей программе.

Другим способом обойти это ограничение языка было бы использование hana::lazy для ветвей. Однако это подходит только тогда, когда ветви не слишком сложны. С hana::lazy вы можете написать предыдущий пример как

template <typename T>
struct pointerize : decltype(
hana::eval_if(hana::traits::is_pointer(hana::type_c<T>),
hana::make_lazy(hana::type_c<T>),
hana::make_lazy(hana::traits::add_pointer)(hana::type_c<T>)
))
{ };
Parameters
condУсловие, определяющее, какая из двух ветвей выбрана.
thenВыражение, называемое eval(then), если cond, является истинным.
else_Функция, называемая eval(else_), если cond имеет ложное значение.

Example

// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
namespace hana = boost::hana;
// eval_if with heterogeneous branches and a Constant condition
BOOST_HANA_CONSTEXPR_LAMBDA auto safe_make_unsigned = [](auto t) {
return hana::eval_if(hana::traits::is_integral(t),
hana::make_lazy(hana::traits::make_unsigned)(t),
hana::make_lazy(t)
);
};
BOOST_HANA_CONSTANT_CHECK(safe_make_unsigned(hana::type_c<void>) == hana::type_c<void>);
BOOST_HANA_CONSTANT_CHECK(safe_make_unsigned(hana::type_c<int>) == hana::type_c<unsigned int>);
// eval_if with homogeneous branches and a constexpr or runtime condition
BOOST_HANA_CONSTEXPR_LAMBDA auto safe_divide = [](auto x, auto y) {
return hana::eval_if(y == 0,
[=](auto) { return 0; },
[=](auto _) { return _(x) / y; }
);
};
int main() {
BOOST_HANA_CONSTEXPR_CHECK(safe_divide(6, 3) == 2);
BOOST_HANA_CONSTEXPR_CHECK(safe_divide(6, 0) == 0);
}
constexpr auto boost::hana::if_

#include <boost/hana/fwd/if.hpp>

Initial value:
= [](auto&& cond, auto&& then, auto&& else_) -> decltype(auto) {
return tag-dispatched;
}
constexpr auto then
Sequentially compose two monadic actions, discarding any value produced by the first but not its effe...
Definition: then.hpp:36

Условно возвращайте одно из двух значений на основании условия. В частности, , затем возвращается iff cond является истинным значением, а else_ возвращается в противном случае. Обратите внимание, что некоторые Логические Модели могут позволять , затем и else_ иметь разные типы, в то время как другие могут требовать, чтобы оба значения имели один и тот же тип.

Parameters
condУсловие, определяющее, какое из двух значений возвращается.
thenЗначение, возвращаемое при cond, является истинным.
else_Значение, возвращаемое при cond, является ложным.

Example

// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
namespace hana = boost::hana;
static_assert(hana::if_(true, 1, 2) == 1, "");
static_assert(hana::if_(false, 1, 2) == 2, "");
static_assert(
hana::if_(hana::true_c,
hana::make_tuple('t', 'r', 'u', 'e'),
hana::make_tuple('f', 'a', 'l', 's', 'e')
)
==
hana::make_tuple('t', 'r', 'u', 'e')
, "");
int main() { }

Ссылка на boost::hana::literals::operator""_s().

constexpr auto boost::hana::not_

#include <boost/hana/fwd/not.hpp>

Initial value:
= [](auto&& x) -> decltype(auto) {
return tag-dispatched;
}

Отрицает логическое . Этот метод возвращает Logical с тем же тегом, но истинное значение которого отрицается. В частности, not_(x) возвращает неверно оцененный Logical, если x является истинно оцененным Logical, и истинно оцененным в противном случае.

Example

// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
namespace hana = boost::hana;
BOOST_HANA_CONSTANT_CHECK(hana::not_(hana::true_c) == hana::false_c);
static_assert(hana::not_(false) == true, "");
int main() { }
constexpr auto boost::hana::or_

#include <boost/hana/fwd/or.hpp>

Initial value:
= [](auto&& x, auto&& ...y) -> decltype(auto) {
return tag-dispatched;
}

Возвращаем, является ли любой из аргументов истинным. или_ можно назвать одним аргументом или более. При вызове с двумя аргументами or_ использует метку-диспетчеризацию, чтобы найти правильную реализацию. Иначе.

or_(x) == x
or_(x, y, ...z) == or_(or_(x, y), z...)

Example

// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
namespace hana = boost::hana;
BOOST_HANA_CONSTANT_CHECK(hana::or_(hana::false_c, hana::false_c, hana::true_c));
BOOST_HANA_CONSTANT_CHECK(!hana::or_(hana::false_c, hana::false_c, hana::false_c));
int main() { }
constexpr auto boost::hana::while_

#include <boost/hana/fwd/while.hpp>

Initial value:
= [](auto&& pred, auto&& state, auto&& f) -> decltype(auto) {
return tag-dispatched;
}

Примените функцию к начальному состоянию, пока некоторый предикат удовлетворен. Этот метод является естественным расширением структуры языка , в то время как для манипулирования состоянием, тип которого может изменяться от одной итерации к другой. Однако обратите внимание, что наличие состояния, тип которого изменяется от одной итерации к другой, возможно только до тех пор, пока предикат возвращает логическое , истинное значение которого известно во время компиляции.

В частности, while_(pred, state, f) эквивалентно

f(...f(f(state)))

где f повторяется до тех пор, пока pred(f(...)) является истинным значением Logical.

Parameters
predПредикат, вызванный состоянием или результатом применения f определенное количество раз в состояние, и возвращать, следует ли применять f еще один раз.
stateНачальное состояние, при котором применяется f.
fФункция, которая повторяется в начальном состоянии. Обратите внимание, что тип возврата f может изменяться от одной итерации к другой, но только тогда, когда pred возвращает время компиляции Logical. Другими словами, decltype(f(stateN)) может отличаться от decltype(f(stateN+1)), но только если pred(f(stateN)) возвращает время компиляции Logical.

Example

// Copyright Louis Dionne 2013-2016
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
#include <vector>
namespace hana = boost::hana;
using namespace hana::literals;
int main() {
// while_ with a Constant condition (loop is unrolled at compile-time)
{
std::vector<int> ints;
auto final_state = hana::while_(hana::less.than(10_c), 0_c, [&](auto i) {
ints.push_back(i);
return i + 1_c;
});
// The state is known at compile-time
BOOST_HANA_CONSTANT_CHECK(final_state == 10_c);
BOOST_HANA_RUNTIME_CHECK(ints == std::vector<int>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
}
// while_ with a constexpr or runtime condition (loop is not unrolled)
{
std::vector<int> ints;
int final_state = hana::while_(hana::less.than(10), 0, [&](int i) {
ints.push_back(i);
return i + 1;
});
// The state is known only at runtime, or at compile-time if constexpr
BOOST_HANA_RUNTIME_CHECK(final_state == 10);
BOOST_HANA_RUNTIME_CHECK(ints == std::vector<int>{0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
}
}

Статья Boost.Hana: Logical раздела может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 19:54:10/0.010443925857544/0