V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  netabare  ›  全部回复第 3 页 / 共 42 页
回复总数  821
1  2  3  4  5  6  7  8  9  10 ... 42  
337 天前
回复了 buynonsense 创建的主题 程序员 临近毕业的开发程序员如何提升自己?
你说的提升是面向就业(短期,三个月这种)还是出于兴趣?
337 天前
回复了 nnegier 创建的主题 程序员 可以讲下你看到的编程语言的美吗?
@Orlion 诶,感谢指路(似乎发现了好东西)
我感觉还有容易断线、断线的时候会一直反复读条加载,还有玩到一半直接卡回桌面。
@letianqiu
@glcolof

打算做的是纯函数式编程,语法接近 ML 系或 Scala 或 Haskell (肯定没那么精简,我也在看哪些语法叠起来相对简单而不那么容易产生二义性)。长期的规划以后再看了,也许可以添加少量 OOP 功能,但肯定不是 Java 的路子。

话说过来 C++、C#和 Java 的解析难度是跟那个泛型标记有关吗?如果从我的路子讲倒是不太用担心这个问题……?毕竟按照 ML 系的类型推导,大部分时候并不需要显式标记类型,类型标记语法也可以设计成避免出现尖括号嵌套这种情况的样子。不过我不确定其他地方是否会遇到别的 LL(1)无能为力的例子了。

@Orlion

手写 LR 也可以的吗?记得都说 LR 语法好像不太适合手写反而比较适合 pg ?但我并不打算现在就考虑设计 pg 的事情。

@ftfunjth

我这边的话,快速实现可能就靠 parser combinator 了,这倒是我原先就有的路径,快速出活是真的快,但这也就回到题目里面的问题……快速写完原型后,这个原型能够长期不断扩展嘛?推倒重写应该还是基于相似的技术选型和路径,而不是说比如今天用了 flex+bison 明天改成 parsec 这样的吧。主要担心的是会不会到头来推倒重做的难度会后期逐步加大,然后语法已经复杂到重构的代价很高昂了。

@qieqie

我没有说「 parser generator 」搞不定我的语法,我想说的是我不太想用 parser generator ,因为不想被技术栈限制或者被 pg 的设计思路所限制(这是以前我遇到过的问题)。

@mahaoqu

也就是说这个问题的回答可以说是「手写递归下降足以覆盖」嘛?明白了,多谢。

@w568w

是在实现自己的语言……目前的进度大概是跳过了 parser ,实现了个解释器和类型系统。大概是计划三五年做出能真的用得上的语言(而不是简单的 toy lang )。

@jones2000

我做的流程应该相当标准了,只是我觉得其他部分跟我想讨论的话题无关而已。解释器和类型系统我都实现了,但为了节省时间我直接跳过了 parser 步骤手写了百来个硬编码的 AST 树的测试用例,然后现在想回过头复查一下 parser 话题而已。至于中间代码和优化那些又是别的话题了。
351 天前
回复了 aqtata 创建的主题 C++ 这种情况如何消除几百个 if/else
用表驱动和高阶函数会比较好
353 天前
回复了 exploretheworld 创建的主题 Java 项目全部是 map 传参
我之前在做 PL 作业的时候,也发现用 Java 的 Map 可以起到类似闭包作用域的作用。
353 天前
回复了 cj323 创建的主题 程序员 函数式编程适不适合游戏开发
游戏开发需要处理大量对象吧,似乎是计算密集型的任务,纯函数式编程会产生巨量生命周期极短的对象,似乎和游戏开发不太合得来。

不过要是写小型游戏或者后端应该会很爽,而且 FRP 和 Signal 之类的工具表现力也很强。不过能不能习惯就是另一个问题了。
AI 补光灯是啥,没听说过
@zhuisui 这个也是我的想法倒是…如果非要使用副作用的话
@zhuisui
@mahaoqu

感谢讲解!

所以这里我想我可能陷入了一个很大的误区,就是把 visitor 机械地等同于 pattern matching ,然后把 pattern matching 在 subtyping 下面的作用(也就是 dynamic dispatch )给搬运到了 visitor pattern 下,但是这个 dynamic dispatch 只是 visitor 的其中一个「稍微显著但不是主要的作用」,而 visitor pattern 的主要作用,就像 @mahaoqu 说的,「面向对象把一个类的多个方法放在一起,而 Visitor 模式恰好反过来了」,或者 @zhuisui 所说的「多个 visitor 分别代表可以输出不同形式的业务逻辑,visitor 之间是互相独立的」这样的作用。

我的理解是你说的「避免了业务侧用 switch case 做模式匹配,仅需 iterate elements 的 accept 方法即可完成调用」用我前面的表达其实就是「 dynamic dispatch 」对吧。比如用户解析 Tree 的时候,它不关心 Tree 具体长啥样或者具体怎么解析,它只希望能够正确的拿到 XML/JSON 或者 Table 。那么 visitor pattern 就充当了这个解析的作用吧。

这么说来倒是很多东西都说得清了。

至于副作用的问题,主要是我对这样的代码有很大的意见:

```java
class ... {
/* L.18 */ResultType result;

public void visitSomeArm(...) {

/* L.259 */ ResultType oldResult = result;

/* L.343 */ ... = visitAnotherArm(...);

/* L.569 */ result = ...;
}

public void VisitAnotherArm(...) {
/* L.1982 */ result = ...
}
}
```

当然这个其实并不是 effectful visitor 的问题而更像是某种 structural 的 code smell 了,如果一个 visitor 的实现能够把副作用清晰的表达出来,让我能够人肉去建模出 Effect 大概长啥样,我对这样的代码并没有太大的意见。

顺祝新年快乐!
2025-01-02 20:15:25 +08:00
回复了 kran 创建的主题 Java 你喜欢使用 Java 下的哪个 web 框架?
不喜欢,但如果我自己选我不会选 Java 。非要用的话可能会考虑 Javalin 、vertx 或者 Ktor 吧。
2024-12-30 18:57:13 +08:00
回复了 freesun165 创建的主题 git 求助 git 自动 merge 丢代码
所以不要把 master 合入分支,分支上面只用 rebase 。
另外关于返回值,我给出的`ISomeVisitor`本身是用 T 去参数化限定它的返回类型。但是在业务代码里面返回类型受限应该不是什么问题?考虑一个复杂数据类型树的话,那么用泛型和返回值可以让 visitor 的职责更清晰吧?

比如说一个订单和用户还有交易构成的数据类型的话,定义三个 visitor ,比如说

```java
class OrderVisitor extends ISomeVisitor<IOrder>
class TxVisitor extends ISomeVisitor<ITx>
class UserVisitor extends ISomeVisitor<IUser>
```

然后自然而然每个 visitor 的 visit 和 accept 分支都会遵守「这个 visitor 只处理和基类相关的职责」,最后再把 visitor 互相组合起来,不会比一个巨型的,什么都可以做的 visitor 更加清晰可维护吗?

A little Java, A few Patterns 里面也有提到 visitor 之间可以组合嵌套的例子。

我在实际代码里面就遇到过那种「一个巨型 visitor 同时承担很多职责」的例子,比如一个 ColorScheme 相关的 visitor 里面不光处理颜色,还要处理字体、排版,甚至把 cache 相关的函数都塞进去的情况。我不觉得哪怕在业务工程的考虑上这样的代码也比使用返回值和泛型参数的代码更好维护。
@mahaoqu 话说 Expression Problem 有什么具体的文章或者讨论可以指一下路吗?我想看看。

@lesismal 我倒是对设计模式并没有怎么太去学,但是造自己语言思考语义和语法的时候,再结合平时的日常工作,就不免会回过头想这些问题。

@zhuisui 换句话说就是 visitor 从设计模式的角度讲只追求分离,并不在乎 dynamic dispatch 和子类型的运行时自动求解,对吗?如果从这个角度讲,那么似乎说得过去……但这么一来这个设计模式对我的兴趣可能也就剩不下多少了。

@GiantHard 我觉得你说的有道理。感谢。
@sagaxu 没必要抠字眼,我人在国外平时用的都是 visitor ,没那么熟悉中文定义。再说我这篇贴子里有提到一个 observer ?
@w568w
@nightwitch


我能想到的一个「针对依赖访问状态」的解决办法就是加一个中间层来表达顺序访问或者访问次数等信息,最简单的实现可以是这样的:

```java
interface IResult<T> { ... }
class Skip<T> implements IResult<T> { ... }
class Ok<T> implements IResult<T> { ... }
```

这只是个例子,不代表说一定要这么做。当然实际业务代码几乎没人这么做就是,但我想说的是这个「依赖访问状态」并不是无解的。

而且换过来说,业务代码里增加不同的层级和抽象不也是很经常的手法嘛? Stream 或者第三方库里面这样的工具也很多,为什么在 visitor 模式上反而不能够通过增加一层来解决问题了呢?引入可变状态和副作用的代价就不需要考虑一下吗?

而且很多场景,例如我自己参与维护的一些代码项目里,经常在重构的时候需要花大量时间理解修改状态和副作用到底怎么桥接起来,最后发现很多代码实际上压根不依赖于访问状态,也对访问顺序没有任何前提要求,看起来更像是单纯的惯性所致,而且这样的 visitor 见一两个也就算了,项目里到处都是而且平均 LOC 几百上千行的时候,连重构都难下手。这也是我提出这个帖子的问题的原因——以前我是默认「 visitor 模式需要有状态,只是我不懂,可能工程代码里有最佳实践」,但在工作一两年接触了更多工程代码后,现在我对这个定论更多持有怀疑态度。

反过来说,以教育和入门为目的的代码样例,例如大学 OOP 课上的代码,也是出于「 visitor 需要依赖可变状态」的前提来设计代码实现的吗?

这里的问题在于,副作用、修改全局变量这些本质上都是不可推理并且无法用类型等东西来建模的,固然可以用单元测试来「告诉使用者这个 API 怎么使用」,但作为一个同时维护和使用 API 的开发者,我对这样的代码有极大的不适感。
1  2  3  4  5  6  7  8  9  10 ... 42  
关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   2401 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 33ms · UTC 05:57 · PVG 13:57 · LAX 21:57 · JFK 00:57
♥ Do have faith in what you're doing.