[Elixir Macro #5] 变量不变
写这篇文章的想法,源自一次关于 and 和 && 区别的讨论。这次开门见山把结论放出来:Elixir 本质上变量是不可变的。
下面开始复现之前的讨论,慢慢解释。同样需要一些知识准备,一个文档两个项目:Reconstructing source code Macro Expansion Sequential Binding
iex(1)> a = 1 1 iex(2)> true and (a = 2) 2 iex(3)> a nil iex(4)> b = 1 1 iex(5)> true && (b = 2) 2 iex(6)> b 2
[Elixir Macro #2]编译时和运行时
首先,先确定下本文中的这两个概念:
编译时:从 mix compile
命令开始到该命令结束,是解释源码,编译过程生成 beam 文件的过程
运行时:VM 运行 beam 中间代码的过程,和源码文件无关
在自己写代码或读别人代码的时候,弄明白一个变量或一个函数是编译时起作用的还是运行时起作用的,非常重要。 Read more »
[Elixir Macro #1]准备工作:defmacro
首先简化问题,先讨论无参数的 defmacro。
defmacro 最常见的用法,看长像是下面这样的:
defmacro m do quote do IO.puts "m" end end
最初学 macro 的时候,给我第一印象就是:defmacro 和 quote 是绑定的。
很显然,这是首先要纠正的东西,因为 defmacro 要求的输出是 AST,只不过恰好 quote 是用的比较多的生成 AST 的方法而已。
生成 AST 的方法还有很多,比如: Read more »
[Elixir Macro #0] 开始
最近发现 Elixir 的宏是个争议很大的东西,有人觉得太复杂没啥用;有人觉得是神器;还有人一直说学不知道干啥的,学不进去。所以想到写这一系列的文章,描述一下我所理解的宏。
其实里面大部分的内容,在之前的一次 meetup 里面提到过,但是当时留下 PPT 之类的资料,这里也算是一个总结。
在此之前,需要有相应的知识准备,官方文档关于宏的介绍:Quote and unquote 和 Macros
以下是目前已经想到的主题,随时更新:
- [Elixir Macro #1]准备工作:defmacro
- [Elixir Macro #2]编译时和运行时
- [Elixir Macro #3]实例分析:plug router
- [Elixir Macro #4]实例分析:plug builder
- [Elixir Macro #5] 变量不变
[Elixir ORM #2] `~>` 和 `<~` 的实现
首先列一下需求吧:
- C/S 模式,需要在当前 node 调用,同时需要在其它 node 通过
:rpc.call
调用 - 在服务端定义的
User
等 module 对应的表,不需要在每个 client 重新定义 - 不希望占用
where
order
insert
update
等常用函数 - 不喜欢 LINQ
所以使用 ~>
和<~
只是因为我实再找不到其它的办法能满足上面的需求。
实现过程中,第一步肯定是调研。我先测试了一下 quote do: User ~> where(id: 1)
,结果是{:~>, [], [{:__aliases__, [alias: false], [:User]}, {:where, [], [[id: 1]]}]}
这说明现 ~> 的优先级还是比较高的,有足够的发挥空间,这么实现是可行的。 Read more »
[Elixir ORM #1] Repo & Model 的 DSL 设计
Ench Repo 逻辑相当简单,只是读配置,用 pooler
起一个池,再给自己定义个 &__using__/1
让 Model 可以 use
它就好了。
和 Ecto 最大的区别,把 Repo 和 Model 做了绑定,一个 Model 只属于一个 Repo,所以调用的时候只需要 User ~> all
而不再是 User |> Repo.all
,代码如下:
defmodule Ench.Model.Repo do defmacro __using__(_) do quote do @config Application.get_env(:ench, __MODULE__) def child_spec do :pooler.pool_child_spec([ name: __MODULE__, init_count: @config[:poolsize], max_count: @config[:poolsize], start_mfa: { @config[:adapter], :start_link, [@config |> Dict.drop [:adapter, :poolsize]] } ]) end def __adapter__ do @config[:adapter] end defmacro __using__([table: table_name]) do quote do use Ench.Model.Table def __table__ do unquote(table_name) end def __repo__ do unquote(__MODULE__) end end end end end end
[Elixir ORM #0] 开始
工作中重度使用 Elixir,之前一直是用 erlport 包装了 sqlalchemy ,但在 Elixir 里用起来却非常不舒服,于是转头去研究 ecto ,然后发现几个地方用起来不太舒服,具体哪里就不在这里吐槽了,总之就是决定自己搞一套类 ORM 的东西,因为个人看来 ORM 和 OO 是绝配,不适合在 FP 中搞,写 maru 的时候从语法级别抄 grape 抄出来的不卫生宏的坑还没填上,所以这次只参考 AR 的 query 语法,其它完全针对 Elixir 语言设计。
基本的语法已经设计完成,因为以下三个原因项目不能开源,所以决定写一系列 blog 记录开发流程。
- 业务针对性很强,几乎没有通用性。
- 使用了 `~>` 和 `<~` 两个运算符,容易和别的库冲突。
- 功能极简,作为微服务设计,单项目使用没有优势。
C#调用C++ dll 调试
C#程序调用C++的库时一直无法断点调试,在网上搜了各种方法,最终解决方法如下:
启动要调试的exe程序
在VS中选择Debug->Attach To Process, 类型选择Native Code, 选择要调试的进程,点Attach,就可以在C++中断点了.
D3D与OpenGL矩阵,行主序与列主序
D3D中使用行主序矩阵,OpenGL中使用列主序矩阵。两者是反过来的。之前一直纳闷为何跨平台的程序中矩阵不需要进行转置操作,今天终于想明白了。
在两种API中,矩阵的内存存储顺序是相同的,只是解释不同。
假设有二维数组 a 表示矩阵, D3D中第 i 行第 j 列的值存储在 a[i][j] 中,GL则存储在 a[j][i] 中,由此抵消了转置。GL中两个矩阵相乘,P = MN,P[i][j] = Mj列 * Ni行。
对点P平移变换矩阵的内存表示为
M =
[1,0,0,0,
0,1,0,0,
0,0,1,0,
a,b,c,1]
D3D中P为行向量,放在前面 P’x = dot( P , Vec4(M[0], M[4], M[8], M[12]) )
GL中P为列向量 ,放在后面 P’x = dot( Vec4( M[0], M[4], M[8], M[12]), P )
结果是相同的。