我正在从《Head First Design Patterns》学习设计模式,为了获得自信,我计划在学习相应的章节后用 C++ 实现每个模式。
关于观察者模式,我真的很难超越语言独立的主要思想。
我浏览过以下内容:
但是,当我开始使用 C++ 进行编码时,一些特定于语言的困难暴露了我对整个主题的一些误解,而我无法通过上述方法解决这些误解。然而,我在这里发帖是因为我有一个(看似)工作代码。
示例代码如下,之后我列出了我对该模式的理解和使用的一些担忧。
#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <unordered_set>
class Observer {
public:
virtual void update() = 0;
};
class Observable {
protected:
std::unordered_set<Observer*> observers;
public:
virtual void addObserver(Observer&) = 0;
virtual void removeObserver(Observer&) = 0;
virtual void notifyObservers() = 0;
};
class Virus : public Observable {
public:
void addObserver(Observer& o) {
observers.insert(&o);
};
void removeObserver(Observer& o) {
std::erase_if(observers, [&o](auto const& …Run Code Online (Sandbox Code Playgroud) 抛开这个问题。直觉上,我已经理解了sequenceA该用例中的作用,但不知道它是如何/为什么这样工作的。
所以这一切都归结为这个问题:sequenceA在以下情况下如何工作?
> sequenceA [("a",1),("b",2),("c",3)]
("abc",[1,2,3])
Run Code Online (Sandbox Code Playgroud)
我看到
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
Run Code Online (Sandbox Code Playgroud)
所以在上面的用例中Traversableis[]和Applicative,因为(,)是一个二进制类型构造函数, is (,) a,这意味着这对被视为其snd字段上的应用函子。这与最终在snd结果中的列表结合在一起。所以我们从一个对的列表到第二个字段中有一个列表的对。
但是"abc"从哪里来?我的意思是,我知道这fst是所有对的串联,但我不知道它是通过++还是通过sconcat的列表fst。sequenceA的签名中似乎没有任何内容可以强制使对中的fsts 可以组合在一起。
仍然必须在某处使用该假设。确实,以下失败
sequenceA [('a',1),('b',2),('c',3)]
Run Code Online (Sandbox Code Playgroud) 在 Hackage 我看到它groupBy的实现是这样的:
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _ [] = []
groupBy eq (x:xs) = (x:ys) : groupBy eq zs
where (ys,zs) = span (eq x) xs
Run Code Online (Sandbox Code Playgroud)
这意味着谓词eq在每组的任意两个元素之间成立。例子:
> difference_eq_1 = ((==1).) . flip (-)
> first_isnt_newline = ((/= '\n').) . const
>
> Data.List.groupBy difference_eq_1 ([1..10] ++ [11,13..21])
[[1,2],[3,4],[5,6],[7,8],[9,10],[11],[13],[15],[17],[19],[21]]
>
> Data.List.groupBy first_isnt_newline "uno\ndue\ntre"
["uno\ndue\ntre"]
Run Code Online (Sandbox Code Playgroud)
相反,如果我想对元素进行分组,使得谓词在任何一对连续元素之间成立,从而使上述结果如下,该怎么办?
[[1,2,3,4,5,6,7,8,9,10,11],[13],[15],[17],[19],[21]]
["uno\n","due\n","tre"]
Run Code Online (Sandbox Code Playgroud)
我自己写的,看起来有点丑
groupBy' …Run Code Online (Sandbox Code Playgroud) 我创建了一个函数m,这样
m "abc" "def" == "bcd"
Run Code Online (Sandbox Code Playgroud)
我想创建另一个函数,用于在给定输入时m生成输出["bcd","efg","hia"]["abc","def","ghi"]
的定义m是
m :: [a] -> [a] -> [a]
m str1 str2 = (drop 1 str1) ++ (take 1 str2)
Run Code Online (Sandbox Code Playgroud) 一个随机的例子:给定以下[Maybe [a]],
x = [Just [1..3], Nothing, Just [9]]
Run Code Online (Sandbox Code Playgroud)
我想f = (^2)通过3层映射,从而获得
[Just [1,4,9],Nothing,Just [81]]
Run Code Online (Sandbox Code Playgroud)
最简单的方法似乎是
(fmap . fmap . fmap) (^2) x
Run Code Online (Sandbox Code Playgroud)
哪里fmap . fmap . fmap就像fmap,但它深入 3 个级别。
我怀疑需要这样的东西,在一般情况下fmap与自己组合给定的次数,并不少见,所以我想知道标准中是否已经有一些东西可以fmap与自己组合一定次数。或者也许是“知道”它应该fmap根据输入与自己组合多少次的东西。
我在复合模式上看了这个视频,其中主要的例子是如何使用模式作为一种手段,从描述待办事项列表的树结构中生成 HTML 代码,其中每个项目都可以依次成为待办事项列表,看起来很方便测试台,所以这里是一个目标 HTML:
[ ] Main
<ul>
<li>[ ] 1.</li>
<li>[ ] 2.
<ul>
<li>[ ] 2.1</li>
<li>[ ] 2.2</li>
</ul>
</li>
<li>[ ] 3.</li>
</ul>
Run Code Online (Sandbox Code Playgroud)
(对不起,如果顶部[ ] Main没有意义,但我不知道 HTML;此外,我相信这与我的问题无关。)
我知道设计模式主要是一个面向对象的“东西”,但是我经常参考Haskell 中的设计模式一文来了解如何在函数式编程中重新解释它们,目的是在更深层次上理解它们。
关于复合模式,那篇文章基本上是这样写的:
合成的。递归代数数据类型。特别突出,因为没有内置继承。
因此,我认为在 Haskell 中尝试它会很容易,并且我想出了以下代码:
import Data.List (intercalate)
data Todo = Todo String | TodoList String [Todo] deriving Show
showList' :: Todo -> String
showList' (Todo s) = "[ ] " ++ s
showList' (TodoList s ts) …Run Code Online (Sandbox Code Playgroud) haskell functional-programming composite category-theory monoids
我看到一些代码示例,其中用于实例化模板函数的类型std::declval被指定为引用类型而不仅仅是类型,如下所示:
std::declval<T &>()
Run Code Online (Sandbox Code Playgroud)
与以下相反:
std::declval<T>()
Run Code Online (Sandbox Code Playgroud)
T某种类型在哪里。我忽略了为什么可以选择引用符号而不是普通类型的微妙之处。有人可以向我解释一下吗?
我知道它std::declval会扩展为,typename std::add_rvalue_reference<T>::type但我仍然不明白为什么人们会通过引用类型而不是普通类型本身来实例化后者。
在实际代码中,构造函数可能非常复杂,因此emplaceastd::optional可以查询可选本身的状态。当这种情况发生时,它通常会更复杂一些,但这是一个(人为的)最小示例:
#include <iostream>
#include <optional>
struct Thing {
Thing();
};
std::optional<Thing> othing;
Thing::Thing() {
std::cout << othing.has_value() << std::endl;
}
int main() {
othing.emplace();
}
Run Code Online (Sandbox Code Playgroud)
对于所有三个主要的标准库实现,这将产生false. 换句话说,在嵌入构造函数的执行过程中,可选项不包含值。
从实现的角度来看,这意味着首先就地构造值,然后设置一些簿记变量。这确实是MSVC、libc++和libstdc++ 的情况。
[...] 当 的实例
optional<T>包含一个 value 时,这意味着 T 类型的对象,称为可选对象的包含 value,在可选对象的存储中分配。
有趣的是,这并没有使用所包含对象的明确定义的生命周期(我想是因为这可以从外部强制结束,例如othing->~Thing()),而是在存储中分配的术语。该标准通常在动态内存的上下文中谈论分配,在这些情况下,分配显然发生在构造之前。
所以我认为提到的标准库实现不兼容,簿记变量应该在构建开始之前设置。
在这种假设下,仍然存在一个问题:实现是否比标准更好,而不是后者需要修复或澄清?我不这么认为:在构造大对象期间,访问已经初始化的子对象是完全有效且常见的,因此这对于包含在可选对象中的对象也应该是可能的。
那么我的考虑是否正确?或者我对分配范围内的术语的解释不正确?是否有反对我的 RightThing™ …
标题应该能澄清我的困惑。我想用这个问题来获得全面的答案,这有助于我理解逗号运算符如何decltype在 SFINAE 上下文中使用。我的这个问题中显示了一个示例用法。
搜索is:question [c++] [decltype] comma,可以找到以下 6 个问题:
\n(2011) How is decltype Should to work withoperator,有一个很有前途的标题,但它是 VisualStudio 特定的,并且唯一的答案从根本上声称编译器中确实存在错误。
(2012)如何从自动返回类型推导出类型?接受的答案如下,参考表达式decltype( ( bool( fun(v[0] ) ), void() ) ),
\n\n\n
\n- 该表达式实际上
\nbool( fun(v[0] ) )并未被求值,因为我们处于非求值上下文中(,类似于)。这里重要的是,如果整个表达式被求值,它将被求值,因此,如果子表达式无效,则整个表达式也无效。decltypesizeof- \n
void()并不是真正的值,但它的行为类似于逗号运算符和上下文中的值decltype。
我还没有真正理解。
\n(2014) c++11 decltype …
c++ decltype sfinae comma-operator template-meta-programming
我指的是这个:
#include <utility>
template<typename T, typename = std::enable_if_t<std::is_rvalue_reference_v<T&&>>>
auto f(T&&) {}
int main(){
int i{};
f(std::move(i)); // ok
f(int{1}); // ok
f(i); // compile time error, as expected
}
Run Code Online (Sandbox Code Playgroud)
还有其他更短的方法可以完成同样的任务吗?
有那么一刻我以为这样的东西可以工作
template<typename T>
auto f(decltype(std::declval<T>())&&) {}
Run Code Online (Sandbox Code Playgroud)
但 IDE 告诉我无法推断模板参数 ' T',我在非推导上下文部分中验证了-说明符的表达式确实是非推导上下文。decltype
我也对c++17解决方案感兴趣(如果存在)。