V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  secondwtq  ›  全部回复第 59 页 / 共 123 页
回复总数  2442
1 ... 55  56  57  58  59  60  61  62  63  64 ... 123  
2020-03-15 18:30:20 +08:00
回复了 keroppi 创建的主题 JavaScript 新手请教链式调用怎么实现按顺序来执行
楼主的需求是可以实现的,比如可以引入某种形式的回调
当然回调写多了就会考虑如何简化抽象
然后就会重新发明 Promise

“专业”的东西的存在是有其道理的,难度最低的路恰恰就是“专业”的路。忽视“专业”的成果自作聪明班门弄斧往往适得其反
当然楼主的需求可能走不到“回调写多了”那一步,那直接回调可能更直接一点
2020-03-14 21:16:34 +08:00
回复了 Lamlam147 创建的主题 奇思妙想 未来“无中生有“”的技术可否实现
噢对了,补充一个(写迷糊了刚才 ...),楼主提到的“电影”和“游戏”中还有一个要素是音频。我上面讨论了图像,音频的情况其实是类似的——很早很早之前就有 MIDI 了,所以音频也是可以“矢量化”的。

但是这就又有一个问题,听觉这东西比视觉要玄学得多。比如现在应该还有很多人能听出计算机合成的音乐和实录的音乐的区别 ... 并且合成音乐是不是要依赖于大量的采样素材呢(我并没有接触过所以并不清楚)?音乐圈个别人有一种歪风邪气,就是你这个东西必须是实录的,不是实录的就是骗子。并且对于某些音乐来说,音乐的“演绎”本身就是一种“再创作”,本身就是一件非常值得推敲的事情。注意这些问题同样适用于电影和摄影等,毕竟能够记录图像和音频的技术都是现代才有的,相关的产业也是很晚才发展起来,之前都是现场听现场看。
就是说吧,对于某些文艺工作者来说,他们的追求之一就是那些“无关紧要的细节”,甚至于“胶片味”“模拟味”,做出的东西总要手调一番才满意(包括至今依然有人采用传统方式做 VFX )。“位图编码”提供了极高的灵活性,这是程序生成无法满足的。

最后上面把许多问题归约到了“程序生成”上,而楼主有说“极少极少的代码”,就算有一部完全程序生成的电影,那需要多少代码来生成?一个 hint——“程序生成”无法再被归约,“程序生成”往下走还是“程序生成”,也就是说程序可以生成程序。最简单的就是编译器,在 C++ 的模板上体现的尤其明显——具体 Google "c++ template code bloat"。但是无论用什么技术手段,最后都落在“抽象”上,也就是如何更好地表示问题。良好的抽象离不开良好的 Composability。
当然“极少极少”是不太可能的——因为这个问题的 Intrinsic Complexity 本身就是极高的。
2020-03-14 20:49:10 +08:00
回复了 Lamlam147 创建的主题 奇思妙想 未来“无中生有“”的技术可否实现
但是注意,我刚才说了 Houdini 是一个”全栈“的软件,也就是说从模型到贴图到动画、特效、渲染、合成都可以做,但是 Procedural 为主的这些软件,Houdini,Substance Designer,Nuke 之类的加起来只占了整个工作流的半壁江山,工作流中还有一部分尚未被 ”Proceduralize“。刚才说了 Procedural approach 的种种优势——占用空间小,可定制性高,方便返工修改等等等等,为什么不全面使用它,就很有意思了。
比如,建模这一过程依然被手工建模统治着——当然个别模型可以自动生成,比如地形、植被树木、部分建筑和道路等的程序生成技术已经十分成熟并且被广泛应用,但是当涉及到主角角色的建模、主角拿的枪模、主角开的车模时还是要从头开始手工搞,当然有许多工具可以提高这一过程的效率,但是并没有哪个做到了哪怕三分之一的程序化。

上文提到在位图贴图中存在大量的冗余信息,而 Procedural Texturing 可以把这些信息,把贴图中包含的规律以简洁正确的方式表示出来(虽然自然界很多表面看起来无规律,但这个”无规律“并不是绝对的)。对于模型来说。这些很多是不成立、不可行的。一般模型本身不大,并且其中的冗余信息是很少的——举个例子就是你要做一个人形角色,它的身体上存在大量的曲线,你把这些曲线向计算机描述出来,模型就建好了,这个描述的过程就是建模的过程,几乎没有可以简化的地方——当然可以做一个参数化生成程序,指定身高、三围、肤色、四肢长度和粗细、手指长度、脖子长度之类的给你生成一个人体,但是脸部你怎么描述?对计算机说”我要这个世界上最漂亮的妹子“然后期望程序帮你画出来,这超出了传统图形学技术的范畴。就更别说现在流行的是各种奇形怪状的”类人“设计,根本无法参数化生成了(想想你在电影和游戏中看到的奇奇怪怪的种族和怪物,或者直接去 https://www.artstation.com 首页逛一圈)。一个模型当然是有规律的——比如很多物体是大致对称的,这个”对称“规律在大多数建模工具中都有体现,也就是你只用编辑一半模型,另一半自动帮你复制,但是”无规律“的那部分,计算机是帮不了你的,而部分种类的模型中这部分尤其的多。
另外一点是,现在 VFX 主流的建模方式是 Polygonal Modeling,这种方式本身是有一些限制的——比如必须保证良好的表面拓扑,模型才能正常使用,而拓扑对于一个 Polygon Mesh 来说一般是全局的,也就是说这种建模方式的 Composability 是很有限的——有点像用 goto 写程序,用锁写共享内存并行,一不小心就会 break 掉奇怪的东西。模型师必须花费额外的精力来维护拓扑。在游戏中问题更严重,游戏不允许太过精细的模型,经常需要最大限度地”优化“模型面数,这就很像编程中的极限性能优化了,这个 ... 是一门技术,也需要模型师的劳动。对于多边形模型(尤其是较为精细的模型)是存在有损压缩算法的(甚至有无损的),但是这个手工优化技术有点像手工压缩 ... 是不是一下感觉又回到了刀耕火种的年代
当然刚才已经做了算力无限这么大的假设,那也可以假设这些限制是可以克服的。但是这并不能动摇刚才的结论。成体系的 Procedural Modeling 方法是有的,其中之一是 CSG ( https://en.wikipedia.org/wiki/Constructive_solid_geometry ),但是用 CSG 来做地球上的动物只会比多边形还要麻烦一个数量级。不过做一个苹果手机应该是很简单的,就算如此,再加上苹果现在能删的都尽量删了,你仍然需要把手机的曲线画出来,把扬声器、天线、充电口、音量键、电源键长什么样子,摄像头如何排布告诉计算机,这些是无法避免的工作。就算是上面大吹特吹的 Procedural Texturing,你仍然需要折腾不同的噪波如何组合,什么效果用什么节点合适 blablabla

换句话说,”创意的产物“是可以被计算机”生成“的,但是”创意“本身还是需要人来”生成“的。这是我们这个”压缩“的极限。
考虑另外一种极端情况,一个大佬手绘的图集,图本身有 4K 分辨率很大,并且是”创意“,按照我们的理论并不能被”压缩“。不这不对,我写了这么长一直在说的就是:对于计算机生成出来的东西,用任何位图编码都会有大量的冗余信息。这个图集其实是“创意的产物”,我们虽然没有“生成”它的程序,但是只要把大佬画图时的每一个笔画都记录下来,在客户端回放一遍就是了 ...
人在消费视频内容的时候,消费的不是这个视频文件本身,在消费游戏内容的时候也不是消费游戏数据,任何的内容追根溯源来自于“无差别的人类劳动”。人在创造内容的过程中使用的工具加入了大量的“细节”,视频和游戏中的冗余内容其实比手绘的静态图要多得多,你的电影和游戏才变得这么大。楼主所说的“无中生有”本来早已经部分实现了,但是并不能完全的“无中生有”,人类依然要去设定那个种子。这个过程虽然变得越来越简单,给人的发挥空间越来越大,人的追求也越来越高(类似 安迪-比尔定律),因此尽管工具越来越先进,“内容创造”这个事情的 Intrinsic Complexity 可能是在变大的。

也就是说理想情况下,电影和游戏可以以“程序+少量的数据”的形式分发(其实都是数据),并且大大地缩减所占空间。下面说说为什么现实不是“理想情况”。
首先这个“理想情况”做了一个假设就是算力几乎无限,这个现在远远没有达到,这很好理解。所以游戏依然是以最大化运行时性能为原则分发的(不要跟我提 DRM,这是下面要说的),就算厂商把生成游戏的“源文件“给你,大多数”高配“ PC 也根本跑不起来,别说 console 了。
然后是整个 Procedural 的生态是碎片化的,我前面提了 Houdini,Substance Designer 和 Nuke 三个产品,但是就算是 Houdini 这样的全栈软件,也有其优势和缺陷。一个完整的工作流中还会用到其他产品,这些产品使用各种奇怪的方式集成在一起,一般不会只用一个产品完全打通”模型到贴图到动画、特效、渲染、合成“的完整工作流,不仅不是单一产品,甚至不是同一家公司的产品(整个产业的基础完全被一家公司垄断怎么都是很可怕的事情)。这样的工作流并不是技术上最优的,只是工程上的局部最优解而已。实际上我作为一个程序员的梦想之一就是完全打通这样一个全栈流程,从而完全发挥 Procedural 的能力。这是另一个技术问题。
最后前面用“源文件“来形容”矢量化“的表示,我觉得这个比喻挺合适的——软件开发也有把源码编译为二进制文件的过程。虽然说很多软件在开发中有开源的考虑,但是绝大多数的电影和绝大多数的游戏就算不以商业成功为目的,但是绝对不会把在终端都完全 Proceduralize 作为目的。让厂商把 Artist 直接产出的源文件给你相当于直接开源电影 /游戏。开源软件尚且不是完全主流,又何谈开源电影 /开源游戏?
(之所以是“绝大多数”,是因为确实有一小撮游戏开发商把 Moddability 作为重要考量(如 P 社),甚至个别游戏根本就是开源的(如 0 A.D.),电影中这种东西更加少见——毕竟游戏虽然某种程度上可以被看做“艺术载体”,但是和字体一样,同时具备“软件”的性质,受开源软件的影响,电影没这福气。但是确实存在”开源电影“——在 18 年前,一个破产的荷兰动画工作室以 GPL 协议发布了它们开发的 DCC 软件的源码,并以基金会形式运作,这就是 Blender,有点像 VFX 界的 Firefox。VFX 界最近也开始受开源的东风影响,Blender 近年迅速发展,并且获得了更多商业上的支持(如 https://news.ubisoft.com/en-us/article/1Fse1XyXzj76UJ1gKFohbz/ubisoft-joins-blender-development-fund-to-support-open-source-animation ),还启动了一些”Open Movie“: https://www.blender.org/about/projects 这些 Open Movie 的源文件全在他们的 Cloud 上(虽然这个 Cloud 为了维护运转是收费的,不过内容是 CC 的)。当然这些”Movie“大多是一些短片,并且应该主要是以技术展示为主,毕竟一个完整的动画长片(并且"票房收入"极少)不是一个开源组织能负担的)

还有一件事我一直没有提,就是上面这些根本就无法应用在依赖于模拟技术的内容上,比如真人电影,真实照片等,对于这些数据,一开始的位图编码会更合适。另外业界除了 Procedural 趋势之外,另一个趋势是”直接从真实世界中采集“,包括高精度的 3D 扫描,和动作捕捉等都也有大量应用,这是和 Procedural 的方式完全相反的。部分是因为我这个回复是基于传统图形学的框架写的,这并不属于它所要解决的问题。其他领域可能有更合适的解决方案,但这就超出我的知识范围了。
2020-03-14 20:48:58 +08:00
回复了 Lamlam147 创建的主题 奇思妙想 未来“无中生有“”的技术可否实现
这个问题很有意思,我从我业余做 VFX 相关的背景出发主要就“动画”和“游戏”等场景讨论一下。

有损的位图信息编码(无论是各种有损的图片压缩算法,还是视频编码(大多数人很难接触到无损视频))是一般用户在存储、传输和消费位图内容时所使用的最主要的载体,它们主要致力于如何在最少影响普通人观感的前提下,去除无关紧要的细节,最小化文件中的冗余。但是我认为,对于我上面提到的场景,主流的编码方式依然浪费了大量的空间在“无关紧要的细节”上,甚至根本就不是“正确”的表示方式。

比如对于一个 CG 制作的天体来说,如《星际穿越》中的黑洞,其”中之人“其实并不是你看到的那些图或者视频,而是一个数学模型。但是这个电影出来之后,人类和计算机都就只能看到一堆像素点了,在截图或转码时,计算机只知道这是一堆像素点,并且按照像素点的方式来处理它,在用力过猛的时候甚至会在色彩过渡的地方出现明显的 banding——本来很简单的一个模型,在变成像素点之后实际上加入了大量并不存在的“细节”——压缩算法需要重新发现这些像素点之间的关系,而显然这重建之后的关系是严重走样的,甚至转码还会加入额外的“细节”。

另一个例子,视频编码算法可以很好地压缩一个 CG 制作的动物镜头,但是同一个动物的另一个镜头,换一个光照环境,换一个视角,换一套姿势,尽管是同一个动物,却需要额外记录大量的信息。它并不能完整地利用这两个镜头之间的冗余关系。

如果我们把“位图”变成“矢量图”,从一开始就直接把黑洞的数学模型,把动物的 3D 模型、动画数据、光照数据和视角数据给到内容消费终端,不但可以节省大量的空间,还具备了无限放大的能力,甚至还可以随便转视角(这可是现在的电影怎么都做不到的),当然对我来说最重要的是还可以通过调整数据和参数进行二次创作。
这里我要做一个假设就是终端的算力是几乎无限的——虽然摩尔定律马上就 55 年了,普通终端可以一秒钟渲染 60 帧甚至 144 帧以上的高质量游戏画面,但是渲染一秒钟的电影级完整 CG 画面依然需要 60 年甚至 144 年以上的时间(数量级不准确,但是你一辈子看不到几帧是肯定的)。不过这个问题本身只要算力上去就很容易解决(所以苏妈成了 Queen of Cinebench ...)
我刚才提到了游戏——你可以认为视频游戏(在本回复里主要指 3D 游戏,尤其是现在有”电影化“倾向的很多 3A 游戏)就是电影的一种“矢量图”形式,以非常简单的眼光来看的话,游戏和电影在很多方面已经很相似了。但是游戏和电影最大的区别之一——“交互”这一点就要求游戏的每一帧画面(除去游戏里的电影)都是在终端机器上实时合成的,并且游戏也可以通过更改场景数据和游戏本体的数据随意更改渲染出的结果。甚至可以说游戏去掉交互就可以变成电影(单指视听方面的相似性,游戏具备更容易修改和二次创作的优势),但是这个“电影”是实时渲染的,如果真的有刚才说的无限算力的话,画质也可以做到和电影齐平。

但是我们知道现在游戏画质做得越来越好,体积一般也越来越大,并且甚至有边际效应递减的趋势(据说游戏体积增长速度没有完全失控主要是受到主机的实体盘容量限制),我们刚才完成了“把电影‘矢量化’并统一到游戏内”的过程,但是这个现实说明刚才假设的”用游戏来‘实现’电影“这一条路很有可能会让最后”电影“成品的体积直接爆炸。对于这个问题,解决方案其实还是不变的——既然电影能”矢量化“,谁规定的游戏不能”矢量化“?
需要指出的是:4K 游戏体积比 4K 电影大是有其合理性的,如果游戏和电影都以”位图“的形式表示,并且最后屏幕上的画质是等同的,那么明显游戏包含的信息比电影要多很多(前面已经提到了游戏具有电影无法做到的无限放大和任意视角能力)。下面还需要做一个假设是构成现在 3A 游戏巨大体积的主要部分是贴图——这个应该是事实,但是我并没有亲自拆现在那些肿瘤一般的游戏的经历 ...(个别游戏可能预渲染电影占很多,不过这个恰好可以通过刚才的”电影游戏化“变成游戏中的其他贴图等数据)。那么既然贴图最多,把贴图”矢量化“,任务就完成了一大半。

这个就很有意思了,现在游戏用的贴图,不就是普通的位图图像嘛!矢量化不就是用矢量图来画么!不过很明显现在很多游戏的贴图并不适合矢量图。但是”矢量化“这种游戏贴图的技术现在已经成熟——就是 Allegorithmic Substance Designer——当然你现在看不到 Allegorithmic 这个名字了,就是因为这是当红炸子鸡,所以前段时间直接被 Adobe 收购了。这个软件要求 Artist 通过节点网络的 Procedural 方式创作贴图(之前主流的方式是用 PS 直接画)——“节点网络”具体是什么样子参见: https://www.artstation.com/artwork/2EJ5Y 这东西登峰造极能做到什么地步参见 https://www.artstation.com/kayv https://www.artstation.com/wermuth https://www.artstation.com/artwork/P9WN1
这种创作方式通过对噪波、分形等数学工具的组合,可以做到乱真的程度,而且这种方法本身并不一定比手绘等传统方法更 inferior——类似结构在自然界中本就是大量存在的,甚至可以说这本就是最贴近“真实”的方式,所以说这东西有颠覆你世界观的可能。

它的“压缩”效果如何呢?简单估计一下吧,一个 4K 三个通道每通道 8bit 的高清贴图可以有 48MB,实际使用的时候三个通道只够漫反射数据用,根据工作流不同还需要若干法线通道、高光通道、高度通道、光滑度通道等等,也就是说一个 4K 贴图的物体贴图动不动就有上百兆,但是这个 Substance 可以用一个 Node Network 生成所有需要的贴图,并且可以随意缩放,还可以调整参数(比如做一个钢铁的材质,节点可以选择暴露一个参数控制锈蚀程度,材质的用户只要把这个参数从 0 调到 1,就完成了一个完整的、连续的钢铁锈蚀的动画——手绘贴图是做不到这一点的)。它只要存储这个 Node Network 就行了,就算一个的 1024*1024 的贴图也有一百零五万像素 * 三个通道,但是 Node Network 是人拉的,人是断然不可能拉一个一百万个节点的网络的。所以一般一个 Substance 文件是 KB 级别,最多不超过 10MB。粗略估算的话,这个相对于位图贴图的“压缩比”至少有 10 倍,一般有好几百倍以上。随着硬件发展,位图贴图分辨率可能会继续提升,位图贴图大小会指数级增加,但是 Substance 文件体积增加要慢得多。

这个东西介绍完了,但是要强调的是 Substance Designer 这个产品本身的开创意义也并没有那么的大——Node Network 在 VFX 工具链中出现得本来就很多,甚至通过 Node Network 来做材质贴图也并非 Substance 首创(并且以前应该就已经用得很多了),但是对手都实在太垃圾(尤其考虑到自动麻将桌部分产品的祖传 crash ),Substance Designer 专注于 Procedural Texturing,做到了非常好的易用性,把这套方法带到了新的高度。
我想通过这个例子引出一个现象,就是以 Substance Designer 为代表,通过 Node Network 的形式暴露给 Artist 的 Procedural 创作方式,最近似乎在 VFX 界出现了一股回潮,并且在很多地方成为了 state of the art。我在 https://v2ex.com/t/636465#r_8459703 这个回复中举了大量的例子,其中有一些可能有二十年以上的历史,有一些则是最近几年的新功能。在这个回复中我还指出了:这种 Node Network 实际上是一个 Functional Program。这个回复上面的一楼还说明了 Composability 的意义,无论是计算机程序还是计算机艺术,背后都是”简单的东西通过组合递归地构成复杂的东西“。

你以为是照片或者手绘出来的,实际上是程序给你画的。

这也很好解释为什么 Substance 对贴图的“压缩比”那么大—— Substance Node Network 就是一段源码,专业的程序员做的项目,一个源文件一般大的几十 K 最多几百 K,整个项目纯源码加起来几十 M 已经很大了,这时候已经可能暴露出工程和项目管理上的一些问题。Artist 作为非专业程序员,能力上限就在那摆着,单个材质整不出这么大。
除了 Substance 之外,Houdini 也值得一提,Substance 只搞贴图,Houdini 是一个”全栈“的软件,就像 3ds Max/Maya 一样,但是 Houdini 完全贯彻了 Procedural 的设计,完全使用 Node Network (以及若干 DSL )工作。这使得 Houdini 极其适合做特效等工作,在自动生成场景、模型、地形等方面也是最好的选择,并成为了这些领域的 State of the art。游戏工业后知后觉,最近也开始使用 Houdini 给你们产出垃圾内容( https://zhuanlan.zhihu.com/p/86991309,https://www.youtube.com/watch?v=NfizT369g60 )(为什么说是垃圾内容,因为某些工作室使用这些技术的目的就是用最少的成本产出最多的内容,从而最大限度延长玩家的游戏时间,甚至有传言说某些游戏的整个任务都是程序生成的,效果还不怎么好,玩家有一种被欺骗的感觉)。
我举的这两个例子离大家并不远,Substance 被 Adobe 收购了,然后你知道 Adobe 的德行,改了订阅制,但是和 JetBrains 一样用一年是可以永久使用的,在 Steam 上有非订阅版本,国区价格很便宜还没八方旅人贵。Houdini 直接有免费的限功能不限时间的 Non-commerical 版本。要想体验门槛并不高。
2020-03-13 18:25:01 +08:00
回复了 guazila 创建的主题 随想 发现自己基本不需要台式 pc 了
哦好吧楼主提了 TW 和骑砍 ...
2020-03-13 18:21:12 +08:00
回复了 guazila 创建的主题 随想 发现自己基本不需要台式 pc 了
Livid 昨天刚刚发了 TD&RA1 Remastered 的新闻

我看这楼也没人提 ...
2020-03-12 20:09:45 +08:00
回复了 Livid 创建的主题 Steam 《命令与征服》的 4K 重制版现在已经可以在 Steam 上预购
关于”想要的‘重制’版“:

中文语境里面”重制“这个词是有一定歧义的,只写”重制“不好理解。我个人倾向于直接用英文 Remaster 和 Remake。

并且 Remaster/Remake/Reforge 三个词的区别也明显比重制 /重铸要明显 ←_←
2020-03-07 00:28:02 +08:00
回复了 maxxfire 创建的主题 Android 个人认为 Gradle 这种构建方式真繁琐
首先,“图形化”和"文本配置“没有本质性区别。

“把配置项列出来”可行性不高——Gradle 配置文件是一个完整的 Groovy 脚本 + 一套 DSL,全都列出来的话估计比 System32 文件夹还大

其次,虽然楼主认为 Gradle 低效,但是 Gradle 实际效率横向比较是不差的——也就是说 Gradle 目前还是比较符合 state of the art 的,目前没有明显更优的选择——或者说“高效图形化配置”这一个领域现在几乎是空白,需要像 Larry Tesler 那种大胆创新。我个人非常欢迎楼主进行自己的探索。

最后,我个人认为更根本的问题出在我们为什么需要如此复杂的规则。Gradle 本身的设计没有什么大问题,但是 Gradle 解决的问题和需求本身可能是错误的。
2020-03-03 18:37:43 +08:00
回复了 Livid 创建的主题 Windows Windows 10 的新图标集
上次看 Fluent Design 的主页还是一两年前,最近应该改过版。现在这个页面貌似就是给你查开发文档和下载 Sketch 模板用的,点进去之后根本不知道这个 Design System 的重点在哪里。老实讲可能还没我之前做的好 ...

我倒是期待微软再把 https://en.wikipedia.org/wiki/Windows_XP#/media/File:Windows_logo_-_2002.svg 这个翻个新放开始按钮的位置。
楼主看这个文档不能学会不是问题,去搜 Tutorial 也是正确的做法

一般开发文档分两大块,Tutorial/Guide (教程)和 Reference (参考),楼主发的这个 Apple 文档链接属于 Reference。
参考是给你在熟练之后日常开发找用法的,就像一门外语的字典。
教程是你学的时候带你入门的,就像一门外语的教科书。

每一个领域都有一套约定俗成的术语和模式,就像一门外语的语法、标音方式之类的。你在看参考之前,需要先看教程熟悉这些术语和模式。上来就翻字典是学不会一门外语的。

楼主对“Apple Developer Documentation”和 w3c.org 两者的类比有一定道理,但是只在“Apple Developer Documentation”只包含 Reference 的情况下成立。我知道 Apple 之前有各种各样的 Guide,一般篇幅都非常的长,也不知道是啰嗦还是详细(很多都是有关 OS X 的(用 OS X 而不用 macOS 是为了暗示时间))。Swift 之类的新东西还有没有就不知道了。
2020-03-02 20:43:37 +08:00
回复了 rapiz 创建的主题 分享创造 我的大一 C 课设, DungeonRush,觉得做的还不错,分享一下~
开源了太可惜,应该上 Steam 的
2020-03-02 19:56:58 +08:00
回复了 ybw 创建的主题 macOS macOS 窗口管理的硬伤
我个人在公司用 Windows,在家(现在)用 Linux,在床上用 Mac。因为必须忍受各种定制性受限的软件,所以对楼主说的这些细节早就不在乎了——比如,我在上个回复中举“窗口标题栏的按钮位置”的例子时,居然要专门打开笔记本确认一下 Mac 的按钮放在哪了。
2020-03-02 19:54:22 +08:00
回复了 ybw 创建的主题 macOS macOS 窗口管理的硬伤
另外一个问题是,楼主在预防“Mac 吹”对本贴内容的污染的时候,出现了逻辑问题(难道是传说中的“Windows 吹”?):楼主把这个所谓“Mac 的逻辑”假设为是“硬伤”“反直觉、反人类、蹩手蹩脚、看不到这种方式设计带来的任何好处”。

并且认为某个人在说“不属于 macOS 的东西”时一定是在说“错误在’用户的逻辑‘”并且"macOS 是宇宙诞生以来唯一完美的存在"。这个逻辑滑坡也很刺激。

也无怪乎本贴下面这么多 Mac 吹试图对楼主进行“矫枉必须过正”了。

---

为什么一个人在指出“某个逻辑不属于 macOS”时,不一定是在说“macOS 是宇宙诞生以来唯一完美的存在”?

鉴于楼主对这个什么 App 切换的问题的体验有争议,咱们就拿所谓窗口标题栏的按钮位置来举例子(因为这个是确定不一样的)。Windows 中,窗口标题栏的操作按钮默认在右边,macOS 中默认在左边。现在假设楼主来问“macOS 窗口标题栏按钮为什么在左边”,并且表示不习惯。

那当然就要告诉你“在左边是 macOS 的逻辑”。macOS 一直以来都是这样设计的,它没有足够的理由挪到右边,楼主如果要想熟练使用 macOS 工作就需要习惯这个逻辑。

↑注意上面这一段仅仅是对事实的陈述,并没有任何的价值判断,更没有“macOS 和 Windows 的‘逻辑’”哪一个更“优越”的隐含意思。

设想一个 macOS 用户来问“Windows 窗口标题栏按钮为什么在右边”,Windows 用户当然也会告诉他 Windows 就是这样的,微软爹叫你怎么用你就得怎么用。

---

我隐约觉得主楼中出现的这些错误,原因是楼主没有把 Windows 和 macOS 当作平等的存在来看待。事实上部分非 macOS 用户对 macOS 用户的偏见和部分 macOS 用户对非 macOS 用户的偏见的程度是不相上下的。

我并不期待能够消除这些偏见(这是不可能完成的任务)。我在 https://v2ex.com/t/552566 这个贴子里面提过一点 PC 和 Mac 两个生态系统历史上的平行关系,但是在我眼里,Windows 和 macOS 两个东西的“平等”,最重要体现在它们都是不可定制的。很多的偏见和先入为主造成的摩擦,都是这个不可定制性的副作用。

Linux (或者说 Free Desktop )生态系统相对于 PC 和 Mac 来说是特殊的存在——因为它是完全可定制的。这样就不存在“习惯”的问题,因为你只需要根据你自己的“习惯”定制你的系统。在界面这一层很少有所谓“Linux 的逻辑”这一说。

Windows 和 macOS 相比而言,其实只是“软爹教你用电脑”和“果爹教你用电脑”,并不存在本质的区别。注意 Windows 和 macOS 并非完全不可定制,但是定制性严重受限,并且你会发现开源的定制工具很少,不是 Shareware 就是 Freeware。

Linux 的定制性也并非无限。我在 https://v2ex.com/t/627912#r_8326554 这个帖子里讨论了“定制性的影响”和“Linux 的所谓可定制性非常有限”的问题。
2020-03-02 19:17:31 +08:00
回复了 ybw 创建的主题 macOS macOS 窗口管理的硬伤
https://blog.yitianshijie.net/2020/02/18/front-and-center-and-switchglass

按照李如一的说法,楼主的逻辑是 Classic Mac OS 用的
所以楼主标题应该换成 Classic Mac OS
2020-02-27 17:56:38 +08:00
回复了 yatseni 创建的主题 Go 编程语言 Go 代码编译为 C 代码
粗略翻了一下 commit history,发现已经做了一年多了 ...

问下 history 里面的 drswinghead,kitech,egitop,pwaller 这几个 ID 背后有几个人?
2020-02-27 17:47:54 +08:00
回复了 Zovven 创建的主题 程序员 现在有哪些领域值得深入呢?我又深深的陷入了焦虑。
绝对领域?
2020-02-27 05:12:07 +08:00
回复了 caowentao 创建的主题 程序员 怎么理解回调函数?
“回调”这个东西,说小了是个 trick,说大了是一种控制流,再大了是一种“计算的组织形式”

你在使用其他代码时,可以看做是对一个“ad-hoc, informally-specified, bug-ridden”的 EDSL 进行编程。比如说一个数组有 count、find、remove 等操作,这是操作数组的 EDSL,但是我需要一个 max 或者 sum 之类的操作,这超过了这个 EDSL 本身的能力,你就得用数组 EDSL 里面一个类似 fold 的操作再结合宿主语言的原语糊上去。不一定是回调函数,在古典时代的 C++ 可能是个 “functor”,在古典时代的 Java 是个 class,但是在这个层面上没有区别。

从程序组织的角度,需要回调(或类似回调的机制)的原因就是你所要解决的问题超出了这个函数 /库(也就是这个 EDSL )本身的能力或范围——比如数组库本身是不关心它的元素是如何 sum 到一块的。再举个例子,GUI 库里面有个很常见的组件叫 Button (按钮),Button 有几个属性可以自定义,比如(按 CSS 的体系) background, font, box-shadow, text-shadow, border-radius, opacity,这是针对 Button 的 EDSL。但是你说这些不够,我想要 Material Design 里面那种 Ripple Effect ( https://codepen.io/Craigtut/pen/dIfzv ),这你就得使用图灵完全的宿主语言来扩展 GUI 库的能力,而扩展的方式就是回调函数或者类似回调函数的某种机制。
古典时代的 GUI 框架一般有上面那些属性,但是表达力不如现在的 CSS,开发者对程序的控制上限更高但也更麻烦,一般会重写一个什么 OnPaint 函数之类的。就算现在的 “H5” 不使用图灵完全的 JS 的话好像最多也只能做成这样: https://codeburst.io/create-a-material-design-ripple-effect-without-js-9d3cbee25b3e

当然如果是 Google 的库,Google 也许会把这个 Ripple 内嵌到 Button 组件里面,这样你不需要写回调也可以用,但是又有新需求:鼠标划过时需要一个 hover light ( https://docs.microsoft.com/en-us/windows/uwp/design/style/reveal )。这个和 Ripple 其实没啥区别,但是组件不提供这个功能,你还得写回调。

围绕 Button 的渲染举例子其实很难,因为 V 站的环境是在讨论 GUI 时首先会扯到 Web 前端,然而现在的 HTML+CSS 已经可以直接实现很多效果。也就是说 HTML 和 CSS 这套 DSL 在 UI 图形这方面已经非常强大——但是就算是如此强大的 DSL,还是没有办法实现所有图形效果,还是需要依赖更加完善的 JS。
但是另一方面,所有的 Button 几乎都会提供一个 OnClick 回调,用户也几乎必然会去用它——用来实现你自己的应用逻辑。GUI 库只关心界面,不关心你的逻辑,所以这个压根不在人家的 scope 里面。

不过这也不是必然的,UI 里面有一个设计模式,就是弹一个模态界面向用户请求一些数据,界面里面一般会有“确定”和“取消”按钮,点了之后就会关掉这个界面。WinForms 直接把这个模式 encode 到了框架中( https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.form.cancelbutton?view=netframework-4.8 ),这里创建了一个 Button,但是不用设置 OnClick,它就会自动关闭对话框,这个行为甚至给 StackOverflow 上的这个开发者造成了困惑( https://stackoverflow.com/questions/1769951/c-sharp-cancelbutton-closes-dialog )。HTML 里面的表单也遵循了类似的思路。但是在写更复杂的逻辑的时候,还是得手动写回调。

总之,人类 ... 哦不 DSL 的能力是有极限的。这并不能怪库,因为第一他们本来并没有完全按照 DSL 的标准去设计,第二 DSL 能力受限是很正常的事情,第三回调机制为这个 DSL 提供了良好的扩展性以及可组合性,这是一个 feature 不是缺陷。

有没有办法避免掉回调函数(或类似于回调函数的机制),又不影响代码的功能呢?当然是可以的,我需要在 A 函数里面调 X 函数,这个 X 我没法确定,所以我给你个回调函数。解决方法:删掉 A 函数,在所有调 A 函数的地方都手动写一遍 A 函数并且把 X 写死在里面,如此递归进行——如果一直保持这样的逻辑滑坡的话,就相当于抛弃掉所有的抽象、模块化和可组合性,项目从一堆 API 的调用,变成看上去像是 BullshitGenerator 生成的超长官样函数的集合,再变成一个巨大的 main 函数 ... 最后到一堆机器码,然后你整天就对着一个十六进制数的矩阵干活
还有一种方法,就是把这个 DSL 做成更完善的形式,这个”方法“的荒谬之处(也是有趣之处)就在于,你做的这个新 DSL 如果具备基本的可用性,那必然会存在类似”函数“和”回调函数“之类的抽象,然后这个 DSL 中 Domain Specific 的那部分又构成新的蹩脚的 EDSL,最后又回到”回调函数“的老路来 ... 并且无论怎么做都不如直接用宿主语言来的方便

---

更广泛地说,回调为程序的组件本身提供了可编程性。这个”可编程性“的必要性我个人深有体会,因为我最开始是通过做游戏 mod 学习编程的,可以参见 https://zhihu.com/question/37994622/answer/79839802。
不过和 RFX 不同的是,我修改的游戏并不具备可编程性——是的就是上面链接里面提到的“Westwood 系的 RTS”,RFX 提到可以修改 “rules.ini 和 arts.ini"。这两个文件其实就是正常的 Windows INI 文件(只不过有点长)。INI 文件里面无非就是 Section, Key 和 Value,也就是说它只是针对特定引擎的一种“ad-hoc, informally-specified, bug-ridden DSL”。虽然也可以玩出很多花样,但是都和简单的”修改生命值“没有本质区别。或者说只能改数据,不能改逻辑。

一般来说,游戏开发者的工作只要把游戏推上市场就行了。moddability 并不是大多数人评判游戏的指标。游戏中的大多数复杂逻辑都会以引擎中的二进制代码的形式存在,外部文件仅仅起一个提供数据的作用。虽然数据也可以是程序,但是在 moddability 对游戏开发者无益甚至会起负面作用( https://forums.totalwar.com/discussion/142609/ca-please-release-map-editing-tools )的情况下,这么做对比传统手段并没有明显优势。(比如理论上 INI 里面可以用各种方式嵌进通用编程语言都没有问题,但是这超出了 Westwood 的技术能力,并且除了玩票之外完全没有必要)

P 社玩出了一点花样,P 社游戏的数据使用 Lua 来存储,但是看起来他们大多数情况下只是把 Lua 当 JSON 来用。值得一提的是 P 社在 Lua 中实现了一个稍微像样的 DSL,云风有一篇 blog 谈过这个问题: https://blog.codingnow.com/2017/07/paradox_data_format.html HOI4 前两年还更新了类似”循环“的功能: https://forum.paradoxplaza.com/forum/index.php?threads/hoi4-dev-diary-modding-and-traits.1117516。现在这个 DSL 不只是个 JSON 了,它有变量,有条件,有循环,理论上能力很强了。但是就算不讨论这个 ad-hoc 的解决方案可能的 bug,这个事情从根上就是有问题的——也许是出于内部工作流程的原因,他们明明使用了 Lua 这个完整的编程语言,但是只用它来存储数据,然后在数据里面糊出了一个蹩脚的 DSL,这个事情本来就是挺难以理解的。

同为 P 社发行的 Cities: Skylines 使用 Unity 开发,并且暴露了 C# 的 Modding API。Minecraft 因为使用 Java 开发所以直接被逆向。这些游戏的可编程性和上面的完全不在一个等级,mod 的空间也大了许多。
但是由于游戏并不是完全开源的,依然会存在各种各样的限制。

以”在缺乏可编程性的平台上实现基本的定制逻辑“为出发点,modder 们尝试过的方案包括但不限于:

* 通过组合引擎已有的功能,以及避开已知和未知的 bug 来实现新逻辑,随便找了个例子: https://www.bilibili.com/read/cv747528 但是由于引擎本身不是可编程的,具有可组合性的逻辑并不多。相当于我给你一个已经放硬了的馒头和一份咸菜,你看看怎么做能好吃一点。
* 通过修改引擎汇编代码来实现新逻辑: https://www.modenc.renegadeprojects.com/RockPatch 这确实能在有较高技术门槛的前提下实现一些”突破“,但是需要手动改 exe,不好实操,更不好长期维护,无法 scale。
* 用 C++ 写逻辑,然后编译成汇编代码,再注入进引擎中: https://github.com/Ares-Developers/Ares/blob/master/src/Ext/Building/Hooks.Prism.cpp 这样维护性比上面的好了很多,出来的成果也丰富得多(文档都写这么长了: https://ares-developers.github.io/Ares-docs/index.html ),但是从源码可以看出来只是不用写汇编,依然需要手动去定位二进制代码注入点和上下文信息
* 和上面的类似,但是目标不是扩展原有引擎,而是渐进式地重写整个引擎——把原来引擎里的函数一个一个地从汇编翻译成 C++,每写一个就用类似的注入方式把引擎里对原函数的调用指向新函数,最后得到重写后的所有源码,这时才可以不依赖原来的文件执行: https://github.com/TheAssemblyArmada/Thyme
(顺便,ZH 引擎光是二进制大小就成倍增长,这个项目存在的大前提是这游戏在发布 Mac 版时(好像外包给了 Aspyr ),忘记 strip 掉 symbol 了,导致只要拉一个 dmg 就能看到所有 mangle 过后的函数名以及 RTTI 信息 ...)
* 换用其他引擎: https://www.moddb.com/mods/mideast-crisis-2/news/final-version-released3
* 直接从头重写,GitHub 上一个 Star 数前 30 的 C# 项目就是这么来的: https://github.com/OpenRA/OpenRA

如果一开始这东西具有基本的可编程性的话,所有这些幺蛾子都不会存在。

比如星际 2 就搞得挺开心的,甚至被调侃”卖编辑器的“( http://bbs.islga.org/read-htm-tid-837191-page-1-fpage-1.html )。当然从 https://us.forums.blizzard.com/en/warcraft3/t/for-all-of-you-aspiring-map-makers/14508 这个来看,应该是玻璃渣老传统了。另外星际 2 的竞技被当作人工智能研究对象应该不是什么新闻,这天然要求一套完全可编程的 API ( https://github.com/BurnySc2/python-sc2 ),不过早在这之前好多年,星际 1 就已经使用类似的手段搞过了( https://bwapi.github.io )。

不过就算是开源项目,也会遇到各种历史包袱造成的定制上的限制。这种部分开放只能说最好情况下只能满足大多数人的需求。但是我混这么多年总结出来的就是:最好的可定制性就是可编程性,而最好的可编程性就是直接开源。很遗憾在开源这个事情上,游戏和 Web 是两个极端。所以我在发现了 modder ”要么一直吃屎,要么成为恶龙“的几乎是被诅咒的命运之后就退坑了。

虽然大家更喜欢使用开源项目的重要原因之一就是开源项目方便定制,但是在一般人是不会去定制大多数用到的开源项目的。(我在 https://v2ex.com/t/627912#r_8326554 这里就讨论过”定制的可能性“”定制的可行性”“可编程性是最好的可定制性”和“开源是最好的可编程性”的问题)所以就算是开源项目中,可编程性也是通过某种形式的“回调”来实现——比如你要扩展 nginx 的功能,一般做法不是直接改 nginx 源码,而是写一个 nginx 插件(其实 Apache 和 nginx 等前端服务器所遵循的“根据配置的位置(‘函数指针’)访问你的静态资源和数据服务”这一模式本来就是一种广义的回调机制)。https://v2ex.com/t/646906 这个帖子讨论了插件机制的实现,哪个方案都离不开回调。

我折腾了这么长,提了几个“避免回调”的方法(全部 inline,完全的 DSL 和完全开源),每一个最后都归于 absurdum。可见回调是非常必要,并且存在非常广泛的一种模式。下面我尝试从更底层的角度说明这一点。

从控制流的角度来看,在“组件 A 回调组件 B”的过程中,控制流先从 A 转移到 B,再从 B 转移回 A。如果我们把自己当作 CPU 来看这个程序的话,相当于 A 和 B 两个组件的代码在交错运行(如果是多个组件相互回调那就是多个组件交错运行,但是这里只考虑两个组件的 base case,多个组件用数学归纳法结论是一样的)。如果就像我们刚才所说的,用某种方式干掉回调机制——也就是说控制流不能 ABA 交叉了。那么控制流会是什么样子?

没有中间的 B 了,那就是完全执行 A 呗?你这个 A 总得有执行完的时候吧。执行完了怎么办?

来看 Node.js 里面著名的回调使用模式:{ someStatement1; someFunction(someArgument, someCallback); }
假设这个 block 位于组件 A 中,而 someFunction 属于组件 B。控制流先从 A 的 someStatement1 转到 B 的 someFunction,在此之后某个时刻可能会转到 A 的 someCallback。著名的“回调地狱”则指一般情况下大量逻辑位于 someCallback 中,并且 someCallback 会递归地嵌套类似的 pattern。
光从控制流来看,这就是上面说的“A 到 B 再到 A”的过程,而我上面没有提到的是这个“A 组件”中的“组件”到底是什么,哪个粒度的。编程语言允许开发者组合很多东西,值,变量,类型。但是函数是特殊的——这体现在当讨论“控制流”时(尤其是上面两段),大概率同时也在讨论“函数”,在讨论“模块”和“组件”时,一般指的是函数的集合——你不会写一个 100 行的函数,然后在这个函数里面分出 6 个“组件”画在架构图上,函数是模块划分的最细粒度。所以不管这个“组件”指的是什么,都绕不过函数。

那么可以尝试把“从 A 组件到 B 组件再到 A 组件”中的“组件”替换成”函数“,就是”从 A 函数到 B 函数再到 A 函数“,而这个可以用更简练的语言表达,就是:”A 函数调用 B 函数“ ...
上面那个代码从控制流角度可以”简化“为:{ someStatement1; var t = someFunction(someArgument); someCallback(t); }
所以楼主要问怎么理解”回调函数“,我觉得不需要理解”回调“,只需要理解”函数“。

如果觉得哪里不对,考虑函数调用在实现上的具体过程:正确实现函数调用需要一个 activation record (可以没有,但是无法处理非 tail-recursive 的函数),activation record 可以在 hardware stack 上,也可以是某种数据结构(或者 hardware stack、寄存器和内存数据结构的某种组合)。这里可能会存储参数、局部变量等,但最重要的是一定会有一个返回地址。
换句话说,在调用函数时,”这个调用返回到什么地方“这个信息被隐式地当作一个参数传进去了。如果没有这个信息,函数执行完之后不知道该怎么返回。函数返回的过程依赖于回调机制。
但是我调用的时候放进去的是个返回地址,并不是个函数啊
那就把它变成一个函数就得了呗
好的现在我们不会传”返回地址“了,而是传”返回函数“,函数的返回相当于调用了一个新函数
恭喜,现在不仅干掉了”回调“的概念,还成功把”函数返回“这个概念也干掉了——”函数返回“和”函数调用“统一了。这就是所谓 Continuation 的概念——这里不太可能完全让你”理解“,我只能说你研究回调最后都会落在这上面
在一个 Continuation-passing Style 的程序中,”函数“永远不会”返回“,而只会在完成自己的任务之后调用”返回函数“
不仅函数可以这么干,条件和循环等控制流语句,甚至一个 primitive 的指令也是同样的道理——x86 中的 EIP 寄存器( arch 书好像更喜欢叫 PC )就相当于当前指令的 continuation,告诉 CPU 这个指令执行完该执行什么,条件跳转则相当于指令参数和 EIP 两个 continuation 挑一个。
在高层,Continuation 也可用于抽象异常、异步、生成器等较高级的控制流。回调函数的一大用途是用来实现”异步控制流“(如上的 Node.js 例子),当前的趋势是通过”协程“来解决类似的问题——再回到底层你会发现”协程“的实现有两种主流模式,”stackless”和“stacked”,但是放在这里来观察,两者的区别不过是做 CPS 变换的抽象层不同罢了。

简单来说,我认为“回调”作为一种机制,是组合不同组件的基础——注意我上面已经把“组件”的概念从模糊的泛指细化到了函数最后又细化到了指令——也就是说“组合性”和“回调”同样广泛。如果没有回调,一个程序只能有一个组件,只能运行一个函数,函数只能运行一个语句,一个语句只能运行一条指令 ... 而“组合”(或者说是分治)是人类解决一切问题的基本方法——先把大问题分解成小问题,小问题再分解成更小的问题,再去解决那些小小的问题 ...
2020-02-26 23:21:14 +08:00
回复了 pems002 创建的主题 Spotify 所以说今天的波动真就因为一个 Podcast?
楼主不说我还不知道 ...
人本来就是不可信的

很多你习以为常的事情也不可信(比如你不能指望每个年都能”正常“过)
包括计算机也是不可信的(就不说分布式系统的容错容灾机制,ECC 内存这种最基础的东西就已经说明了这一点)
2020-02-26 19:03:51 +08:00
回复了 ISVStar 创建的主题 随想 讨论一下订阅制服务的乱象
/r/selfhosted 欢迎你

@Mavious #30 ”玩花样“”一点点杀猪“能叫”没格局“么?其他公司不也这样么,不是”正常的商业逻辑“么?
现在有些人就是这样的逻辑↑
1 ... 55  56  57  58  59  60  61  62  63  64 ... 123  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2928 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 47ms · UTC 10:52 · PVG 18:52 · LAX 03:52 · JFK 06:52
Developed with CodeLauncher
♥ Do have faith in what you're doing.