"Predicates should not modify their state due to a function call" 是什么意思?
What does it mean "Predicates should not modify their state due to a function call"?
我在网上阅读有关 C++ 的内容时遇到了以下语句:
Predicates should not modify their state due to a function call.
我不明白这里的"state"是什么意思。有人可以举例说明吗?
通俗地说,谓词中的状态是数据成员。更改状态的谓词意味着成员在算法执行期间发生更改,并且该更改将影响谓词行为。
避免这种情况的原因是算法没有义务保留谓词的单个实例。它们可能很容易被复制,并且在一个副本中更改状态,不会与另一个副本中的状态共享。结果,程序将出现意外行为(对于期望状态更改生效的人)。
让我们以算法std::count_if
为例。它遍历一个范围并计算给定谓词计算为真的频率。进一步假设我们要检查容器中有多少元素小于给定数字,例如5 或 15。
谓词可以是很多东西。它必须是可调用的。它可以是一个仿函数:
struct check_if_smaller {
int x;
bool operator()(int y) const { return y < x; }
};
您可以创建该谓词的不同实例,例如这两个
check_if_smaller a{5};
check_if_smaller b{15};
可用于检查数字是否分别小于5
或15
:
bool test1 = a(3); // true because 3 < 5
bool test2 = b(20); // false because 20 is not < 15
成员x
是谓词的状态。通常这在应用谓词时不应更改(通过调用其 operator()
)。
来自 wikipedia:
In mathematical logic, a predicate is commonly understood to be a
Boolean-valued function P: X→ {true, false}, called the predicate on
X. However, predicates have many different uses and interpretations in
mathematics and logic, and their precise definition, meaning and use
will vary from theory to theory.
粗略地说,谓词是一个将某些东西映射到布尔值的函数。事实上,我们使用的函子不仅仅是一个函数,而是一个具有状态的函数对象,这可以被视为一个实现细节,并且对于相同的输入重复评估相同的谓词通常会产生相同的结果。此外,算法会做出此假设,并没有真正阻止它们复制您传递的谓词(实际上 the standard explicitly permits them to do so)。如果评估谓词会改变其内部状态,则该算法可能无法按预期工作。
从本质上说,谓词的行为应该像一个纯函数(在数学术语中),即它的 returning 值应该仅取决于输入。
提到状态是因为可以复制谓词或可以在不同线程中调用谓词,这取决于实现和平台行为。对于不是函数的 lambda 和其他可调用对象,这可能意味着对存储的无序访问、通过引用捕获或访问不同的值(如果它们是通过值捕获的)。对于一个函数来说,意味着任何副作用(包括静态变量的改变)都可能导致问题。
如果排序谓词对同一对 return 会产生不同的结果,则某些排序算法将失效。
除了其他答案之外,许多采用谓词的算法不承诺任何特定的遍历顺序(ExecutionPolicy 重载允许交错遍历)。同一个问题,你可能会得到不同的答案。
如果有多个线程调用1 谓词并且它更改了一些共享值,那就是a data race,即未定义的行为。
- 或一个线程交错调用
我在网上阅读有关 C++ 的内容时遇到了以下语句:
Predicates should not modify their state due to a function call.
我不明白这里的"state"是什么意思。有人可以举例说明吗?
通俗地说,谓词中的状态是数据成员。更改状态的谓词意味着成员在算法执行期间发生更改,并且该更改将影响谓词行为。
避免这种情况的原因是算法没有义务保留谓词的单个实例。它们可能很容易被复制,并且在一个副本中更改状态,不会与另一个副本中的状态共享。结果,程序将出现意外行为(对于期望状态更改生效的人)。
让我们以算法std::count_if
为例。它遍历一个范围并计算给定谓词计算为真的频率。进一步假设我们要检查容器中有多少元素小于给定数字,例如5 或 15。
谓词可以是很多东西。它必须是可调用的。它可以是一个仿函数:
struct check_if_smaller {
int x;
bool operator()(int y) const { return y < x; }
};
您可以创建该谓词的不同实例,例如这两个
check_if_smaller a{5};
check_if_smaller b{15};
可用于检查数字是否分别小于5
或15
:
bool test1 = a(3); // true because 3 < 5
bool test2 = b(20); // false because 20 is not < 15
成员x
是谓词的状态。通常这在应用谓词时不应更改(通过调用其 operator()
)。
来自 wikipedia:
In mathematical logic, a predicate is commonly understood to be a Boolean-valued function P: X→ {true, false}, called the predicate on X. However, predicates have many different uses and interpretations in mathematics and logic, and their precise definition, meaning and use will vary from theory to theory.
粗略地说,谓词是一个将某些东西映射到布尔值的函数。事实上,我们使用的函子不仅仅是一个函数,而是一个具有状态的函数对象,这可以被视为一个实现细节,并且对于相同的输入重复评估相同的谓词通常会产生相同的结果。此外,算法会做出此假设,并没有真正阻止它们复制您传递的谓词(实际上 the standard explicitly permits them to do so)。如果评估谓词会改变其内部状态,则该算法可能无法按预期工作。
从本质上说,谓词的行为应该像一个纯函数(在数学术语中),即它的 returning 值应该仅取决于输入。
提到状态是因为可以复制谓词或可以在不同线程中调用谓词,这取决于实现和平台行为。对于不是函数的 lambda 和其他可调用对象,这可能意味着对存储的无序访问、通过引用捕获或访问不同的值(如果它们是通过值捕获的)。对于一个函数来说,意味着任何副作用(包括静态变量的改变)都可能导致问题。
如果排序谓词对同一对 return 会产生不同的结果,则某些排序算法将失效。
除了其他答案之外,许多采用谓词的算法不承诺任何特定的遍历顺序(ExecutionPolicy 重载允许交错遍历)。同一个问题,你可能会得到不同的答案。
如果有多个线程调用1 谓词并且它更改了一些共享值,那就是a data race,即未定义的行为。
- 或一个线程交错调用