【问题标题】:Thoughts on minimize code and maximize data philosophy关于最小化代码和最大化数据哲学的思考
【发布时间】:2010-12-09 11:52:25
【问题描述】:

我听说过最小化代码和最大化数据的概念,并且想知道其他人可以给我什么建议,告诉我在构建自己的系统时应该如何/为什么要这样做?

【问题讨论】:

标签: data-driven


【解决方案1】:

通常数据驱动的代码更易于阅读和维护。我知道我已经看到数据驱动被极端化并且最终变得非常不可用的情况(我正在考虑我使用过的一些 SAP 部署),但是编写自己的“领域特定语言”来帮助你构建您的软件通常可以节省大量时间。

pragmatic programmers 在我的脑海中仍然是我读过的编写小语言的最生动的倡导者。运行很少输入语言的小型状态机可以很多在很小的空间内完成,并且可以很容易地进行修改。

一个具体示例:考虑一个累进所得税制度,税级为 1,000 美元、10,000 美元和 100,000 美元。低于 1,000 美元的收入是免税的。 1,000 美元至 9,999 美元之间的收入按 10% 征税。 10,000 美元至 99,999 美元之间的收入按 20% 征税。超过 100,000 美元的收入按 30% 征税。如果你把这一切都写在代码中,它看起来就像你怀疑的那样:

total_tax_burden(income) {
    if (income < 1000)
        return 0
    if (income < 10000)
        return .1 * (income - 1000)
    if (income < 100000)
        return 999.9 + .2 * (income - 10000)
    return 18999.7 + .3 * (income - 100000)
}

添加新的税级、更改现有的税级或更改税负的税负,都需要修改代码并重新编译。

但如果它是数据驱动的,您可以将此表存储在配置文件中:

1000:0
10000:10
100000:20
inf:30

编写一个小工具来解析这个表格并进行查找(不是很困难,对吧?)现在任何人都可以轻松地维护税率表。如果国会决定 1000 个括号会更好,任何人都可以使表格与 IRS 表格对齐,并完成它,无需重新编译代码。一个括号或数百个括号可以使用相同的通用代码。

现在来说一些不太明显的事情:测试。 AppArmor 项目对加载各种配置文件时系统调用应执行的操作进行了数百次测试。一个示例测试如下所示:

#! /bin/bash
# $Id$

#   Copyright (C) 2002-2007 Novell/SUSE
#
#   This program is free software; you can redistribute it and/or
#   modify it under the terms of the GNU General Public License as
#   published by the Free Software Foundation, version 2 of the
#   License.

#=NAME open
#=DESCRIPTION 
# Verify that the open syscall is correctly managed for confined profiles.  
#=END

pwd=`dirname $0`
pwd=`cd $pwd ; /bin/pwd`

bin=$pwd

. $bin/prologue.inc

file=$tmpdir/file
okperm=rw
badperm1=r
badperm2=w

# PASS UNCONFINED
runchecktest "OPEN unconfined RW (create) " pass $file

# PASS TEST (the file shouldn't exist, so open should create it
rm -f ${file}
genprofile $file:$okperm
runchecktest "OPEN RW (create) " pass $file

# PASS TEST
genprofile $file:$okperm
runchecktest "OPEN RW" pass $file

# FAILURE TEST (1)
genprofile $file:$badperm1
runchecktest "OPEN R" fail $file

# FAILURE TEST (2)
genprofile $file:$badperm2
runchecktest "OPEN W" fail $file

# FAILURE TEST (3)
genprofile $file:$badperm1 cap:dac_override
runchecktest "OPEN R+dac_override" fail $file

# FAILURE TEST (4)
# This is testing for bug: https://bugs.wirex.com/show_bug.cgi?id=2885
# When we open O_CREAT|O_RDWR, we are (were?) allowing only write access
# to be required.
rm -f ${file}
genprofile $file:$badperm2
runchecktest "OPEN W (create)" fail $file

它依赖于一些辅助函数来生成和加载配置文件,测试函数的结果,并向用户报告。扩展这些小测试脚本要比不用一点语言来编写这种功能要容易得多。是的,这些是 shell 脚本,但它们与实际的 shell 脚本相去甚远;)它们实际上是数据。

我希望这有助于激发数据驱动的编程;恐怕我的口才不如其他写过它的人,而且我当然没有擅长,但我尝试了。

【讨论】:

  • 不错的答案...也给了我一些思考
【解决方案2】:

在现代软件中,代码和数据之间的界限可能会变得非常模糊和模糊,区分两者并不总是那么容易。毕竟,就计算机而言,一切都是数据,除非它是由现有代码(通常是操作系统)决定的。甚至程序也必须作为数据加载到内存中,然后 CPU 才能执行它们。

例如,想象一个计算订单成本的算法,订单越大,每件商品的价格就越低。它是商店中更大的软件系统的一部分,用 C 语言编写。

这个算法是用 C 语言编写的,它读取一个文件,该文件包含一个由管理层提供的输入表,其中包含各种每件商品的价格和相应的订单大小阈值。大多数人会争辩说,带有简单输入表的文件当然是数据。

现在,假设商店将其策略更改为某种渐近函数,而不是预先选择的阈值,以便它可以容纳疯狂的大订单。他们可能还想考虑汇率和通货膨胀——或者管理层想出的任何其他因素。

商店聘请了一位称职的程序员,她在原始 C 代码中嵌入了一个很好的数学表达式解析器。输入文件现在包含一个带有全局变量的表达式、log()tan() 等函数,以及Planck constantcarbon-14 退化率等一些简单的东西。

cost = (base * ordered * exchange * ... + ... / ...)^13

大多数人仍然会争辩说,即使不像表格那么简单,表达式实际上也是数据。毕竟它可能是由管理层按原样提供的。

这家商店收到了大量客户的投诉,这些客户在试图估算他们的费用时变得脑残,会计人员也收到了大量关于大量零钱的投诉。商店决定为小订单返回餐桌,并为大订单使用Fibonacci sequence

程序员厌倦了修改和重新编译 C 代码,因此她嵌入了 Python 解释器。输入文件现在包含一个 Python 函数,该函数轮询满屋子的 Fib(n) 猴子,了解大订单的成本。

问题:这是输入文件数据吗?

从严格的技术角度来看,没有什么不同。表和表达式都需要在使用前进行解析。数学表达式解析器可能支持分支和函数 - 它可能不是图灵完备的,但它仍然使用自己的语言(例如 MathML)。

然而现在很多人会争辩说输入文件只是变成了代码

那么将输入格式从数据变成代码区别特征是什么?

  • 可修改性:必须重新编译整个系统才能进行更改是非常以代码为中心的系统的良好指示。然而,我可以很容易地想象(嗯,更像是我实际上见过)软件设计得不够称职,以至于无法拥有例如编译时内置的输入表。我们不要忘记,许多应用程序仍然有图标 - 大多数人会认为 data - 内置在他们的可执行文件中。

  • 输入格式:这是 - 在我看来,天真的 - 人们考虑的最常见因素:“如果它是编程语言,那么它就是代码”。好吧,C 是代码——毕竟你必须编译它。我也同意 Python 也是代码——它一门成熟的语言。那么为什么不是 XML/XSL 代码呢? XSL 本身就是一种相当复杂的语言 - 因此在其名称中使用了 L

在我看来,这两个标准都不是真正的区别特征。我认为人们应该考虑其他事情:

  • 可维护性:简而言之,如果系统的用户必须聘请第三方来获得修改系统行为所需的专业知识可用,那么系统应该在一定程度上被视为以代码为中心。

当然,这意味着系统是否是数据驱动的,至少应考虑与目标受众的关系——如果不是与客户相关的个案- 案例基础。

这也意味着区别可能会受到可用工具集的影响。 UML 规范是一场噩梦,但如今我们拥有所有这些图形 UML 编辑器来帮助我们。如果有某种第三方高级 AI 工具可以解析自然语言并生成 XML/Python 等,那么即使对于更复杂的输入,系统也会变成数据驱动的。

一家小商店可能没有聘请第三方的专业知识或资源。因此,允许员工利用普通管理课程(数学、图表等)所获得的知识来修改其行为的东西对于这些受众来说可以被认为是充分的数据驱动的。

另一方面,一家价值数十亿美元的国际公司通常在其工资单中拥有一群 IT 专家和网页设计师。因此,XML/XSL、Javascript,甚至 Python 和 PHP 都可能很容易处理。它也有足够复杂的要求,简单的东西可能就无法解决它。

我相信,在设计软件系统时,应该努力在所使用的输入格式中实现良好的平衡,在这种情况下,目标受众可以需要做他们需要做的事情,而 不需要 不得不经常拜访第三方。

应该注意的是,外包更加模糊了界限。有很多问题,对于这些问题,当前的技术根本不允许外行人可以接近解决方案。在这种情况下,解决方案的目标受众可能应该被认为是外包操作的第三方。 预计该第三方将雇用相当数量的专家。

【讨论】:

  • 绝妙的答案!真的让我思考应该如何设计我的应用程序。最后,这一切都是为了让最终用户轻松修改我的应用程序!
【解决方案3】:

Rob Pike 提出的 Unix Philosophy 下的五个格言之一是:

数据占主导地位。如果您选择了正确的数据结构并将事物组织得井井有条,那么算法几乎总是不言而喻的。数据结构而非算法是编程的核心。

通常缩写为“编写使用智能数据的愚蠢代码”。

【讨论】:

  • 确实如此。 Niklaus Wirth(Pascal 的发明者)早在 1975 年就写了一本名为“算法 + 数据结构 = 程序”的书
【解决方案4】:

其他答案已经深入探讨了如何经常使用仅对其特定输入模式做出反应的简单代码来编写复杂行为。您可以将数据视为特定领域的语言,并将您的代码视为解释器(也许是一个微不足道的解释器)。

鉴于大量数据,您可以走得更远:统计数据可以为决策提供动力。 Peter Norvig 在Beautiful Data 中写了一个great chapter 来说明这个主题,文本、代码和数据都可以在线获取。 (披露:感谢致谢。)第 238-239 页:

数据驱动的方法与更传统的软件开发相比如何 程序员编写显式规则的过程? ...显然,手写规则很难开发和维护。大 数据驱动方法的优点是数据中编码了如此多的知识, 只需收集更多数据,就可以添加新知识。但另一个优点是 虽然数据可能很大,但代码很简洁——correct 大约 50 行,而 ht://Dig 的拼写代码则超过 1,500 行。 ...

另一个问题是便携性。如果我们想要一个拉脱维亚语拼写纠正器,英语 变音位规则用处不大。将数据驱动的correct 算法移植到另一个 语言,我们只需要大量的拉脱维亚语语料库;代码保持不变。

他使用从 Google 收集的数据集通过 Python 代码具体展示了这一点。除了拼写更正之外,还有用于分割单词和破译密码的代码——同样只需几页,Grady Booch's book 花了几十页甚至没有完成。

"The Unreasonable Effectiveness of Data" 更广泛地发展了相同的主题,没有任何细节。

我在另一家搜索公司的工作中采用了这种方法,我认为与表驱动/DSL 编程相比,它仍然没有得到充分利用,因为我们中的大多数人直到最近一两年才开始大量使用数据。

【讨论】:

    【解决方案5】:

    在可以将代码视为数据的语言中,这不是问题。您可以根据解决方案的需要使用清晰、简短和可维护的内容,并倾向于数据、代码、功能、OO 或过程。

    在程序中,区别是被标记的,我们倾向于将数据视为以特定方式存储的某种东西,但即使在程序中,最好隐藏数据 在 API 之后,或者在 OO 中的对象之后。

    lookup(avalue) 可以在其生命周期内以多种不同方式重新实现,只要它以函数形式开始。

    ...我一直在为不存在的机器设计程序并添加:'如果我们现在有一台包含这里假设的原语的机器,那么工作就完成了。 ...在实际实践中,当然,这台理想的机器将不存在,所以我们的下一个任务——在结构上与原来的相似——是对“上层”机器的模拟进行编程......但这一堆程序是为很可能不存在的机器编写的,所以我们的下一个工作将是根据下一个较低级别机器的程序来模拟它,等等,直到最后我们有一个可以执行的程序我们的硬件...

    E. W. DijkstraNotes on Structured Programming 中,1969 年,由 John AllenAnatomy of Lisp 中引用,1978 年。

    【讨论】:

      【解决方案6】:

      当我想到这个我相当认同的哲学时,首先想到的就是代码效率。

      当我编写代码时,我确信它并不总是接近完美甚至完全知识渊博。了解足够多的知识,以便在需要时接近机器的最高效率,并在其余时间保持良好的效率(也许为更好的工作流程进行权衡),这使我能够生产出高质量的成品。

      以数据驱动的方式编码,您最终会使用代码来实现代码的用途。将每个变量“外包”到文件中将是愚蠢的极端,程序的功能需要在程序中,内容、设置和其他因素可以由程序管理。

      这还允许更多动态应用程序和新功能。

      如果您甚至有一个简单的数据库形式,您就可以将相同的功能应用于许多状态。您还可以做各种创造性的事情,例如根据文件头数据或目录、文件名或扩展名更改程序正在执行的操作的上下文,尽管并非所有数据都必要存储在文件系统上.

      最终将您的代码保持在一个只处理数据的状态,让处于一种更接近于设想实际情况的心态。这也避免了代码的大量使用,大大减少了臃肿软件。

      我相信它使代码更易于维护、更灵活、更高效aaa,我喜欢它。

      也感谢其他人对此的意见!我觉得这很鼓舞人心。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-04
        • 2016-10-04
        相关资源
        最近更新 更多