查看“︁隐式编程”︁的源代码
←
隐式编程
跳转到导航
跳转到搜索
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
{{NoteTA |G1 = IT }} '''隐式'''('''tacit''')编程<ref name="train">{{cite web|title=Tacit programming|url=https://aplwiki.com/wiki/Tacit_programming|access-date=2022-06-11|archive-date=2022-07-20|archive-url=https://web.archive.org/web/20220720180701/https://aplwiki.com/wiki/Tacit_programming}}</ref>,或称为[[函数级编程]],是一种[[编程范型]],也叫做'''无点'''(point-free)样式。其中函数定义不标示所要运算的被称为“[[点]]”的[[参数 (程式设计)|参数]],转而函数定义只是其他函数的[[复合函数|复合]],比如那些操纵参数的[[组合子逻辑|组合子]]。隐式编程有着理论价值,因为严格的使用复合导致程序适配于{{en-link|等式逻辑|Equational logic|等式推理}}<ref name="cunha2005">Manuel Alcino Pereira da Cunha (2005) [http://hdl.handle.net/1822/2869 Point-free Program Calculation]</ref>。隐式编程是特定[[编程语言]]的天然样式,这包括了[[APL语言|APL]]的一些现代实现和方言<ref>W. Neville Holmes, ed. (2006) ''Computers and People''</ref>,和[[串接式编程语言|串接式语言]]比如[[Forth]]。由于缺少参数命名,认为这种风格导致了不必要的晦涩难懂的人,给它起了个[[绰号]]叫做“无意义”(pointless)风格<ref name="cunha2005"/>。 ==APL语言家族== 在一些[[APL语言|APL]]方言中,允许将函数组合入服从几个规则的“列车”(train);这允许建立复杂的派生函数,而不需要显式指定任何参数;实现了列车的APL方言包括:<tt>[[J语言|J]]</tt>语言、Dyalog APL、dzaima/APL、ngn/apl和NARS2000<ref name="train" />。 ===<tt>J</tt>语言=== 在<tt>[[J语言|J]]</tt>语言中,下列在一个数值的列表([[阵列]])上计算平均值的函数采用了一种无参数样式代码: <syntaxhighlight lang=j>avg=: +/ % #</syntaxhighlight> <code>+/</code>通过将求和(<code>+</code>)插入(<code>/</code>)到一个阵列的所有元素之间来计算它们的合计值。<code>#</code>总计一个阵列的元素数目。<code>%</code>用<code>+/</code>这个阵列的结果值除以<code>#</code>这个阵列的结果值。 [[欧拉公式]]<math>e^{ix} = \cos x + i\sin x</math>可隐式表达为: <syntaxhighlight lang=j> cos =: 2 o. ] sin =: 1 o. ] Euler =: ^@j. = cos j. sin </syntaxhighlight> 这里定义的函数<code>Euler</code>在任何输入值上都恒等于<code>1</code>,即这个等式永远为真。其中用到了一些原语(primitive)函数:<code>=:</code>表示全局定义;<code>o.</code>表示[[圆函数]],由左侧名词参数选择具体的函数;<code>]</code>不变动的返回右侧名词参数;<code>^</code>的一元定义为指数函数;<code>j.</code>的一元定义为[[虚数单位]]<code>0j1</code>乘以右侧参数<code>y</code>,而它的二元定义为<code>x + 0j1*y</code>,即组合左侧参数<code>x</code>和右侧参数<code>y</code>成为[[复数 (数学)|复数]],而二者分别是其实部和虚部;<code>@</code>表示数学[[函数复合]];<code>=</code>是[[等于]][[布尔]]运算,两侧参数相等返回<code>1</code>而不等返回<code>0</code>。 ===Dyalog APL=== 上述相同的隐式计算用[[APL语言|APL]]的现代版本Dyalog APL<ref>{{Cite web |url=https://aplwiki.com/wiki/Dyalog_APL |title=Dyalog APL |access-date=2022-06-14 |archive-date=2022-06-28 |archive-url=https://web.archive.org/web/20220628203332/https://aplwiki.com/wiki/Dyalog_APL }}</ref>表达为: <syntaxhighlight lang=APL> avg ← +⌿ ÷ ≢ cos ← 2 ○ ⊢ sin ← 1 ○ ⊢ j ← {⍺←0 ⋄ ⍺+0j1×⍵} ⍝ j函数的定义不是隐式的 Euler ← *∘j = cos j sin </syntaxhighlight> 这里采用{{en-link|直接函数|Direct function}}定义了<code>j</code>函数,其中在<code>{</code>与<code>}</code>之间由<code>⋄</code>分隔的是[[卫语句|守卫]]的表达式序列(这里只有表达式),<code>⍺</code>指示左参数而<code>⍵</code>指示右参数,<code>⍺←</code>指示一元定义需要的缺省左参数。 ==Unix管道== {{main|管道 (Unix)}} 在[[Unix]] [[Unix shell|shell]][[脚本语言|脚本]]中,命令是从[[标准串流|标准输入]]接收数据并发送结果至[[标准串流|标准输出]]的{{en-link|过滤器 (软件)|Filter (software)|过滤器}}。例如: <syntaxhighlight lang="bash"> sort | uniq -c | sort -rn </syntaxhighlight> 可以视为一个隐式或无点复合,它返回它的每个参数的计数和这些参数,并按照这个计数的递减次序来排序。命令<code>sort</code>和<code>uniq</code>相当于函数,而<code>-c</code>和<code>-rn</code>是控制这些函数的选项,但是不提及参数。[[管道 (软件)|管道]]<code>|</code>相当于函数复合算子。下面是其用例: <syntaxhighlight lang="console"> $ echo '2 4 3 1 3 12 2' | xargs -n1 | sort | uniq -c | sort -rn 2 3 2 2 1 4 1 12 1 1 </syntaxhighlight> ==Python== 如下[[Python语言|Python]]代码是对应上节示例中Unix管道中命令的函数定义和一序列的运算: <syntaxhighlight lang="python"> def sort(argv): return sorted(argv, key=str) def uniq_c(argv): counts = {} for i in argv: counts[str(i)] = counts.get(str(i), 0) + 1 return [(v, int(k)) for k , v in counts.items()] def sort_rn(argv): sort_rk2 = sorted(argv, key=lambda x:str(x[1]), reverse=True) return sorted(sort_rk2, key=lambda x:x[0], reverse=True) a = [2, 4, 3, 1, 3, 12, 2] b = sort_rn(uniq_c(sort(a))) </syntaxhighlight> 这里的<code>sort()</code>由于<code>uniq_c()</code>所采用的实现方法不再依赖于它而没有存在必要,只是为了与上节Unix示例保持一致。 基于标准库的[[函数式编程]]工具<code>functools</code>中的{{en-link|部份应用|Partial application}}函数<code>partial()</code>和[[fold (高阶函数)|归约]]函数<code>reduce()</code>,这个例子可以写为无点样式的没有参数的一序列函数的复合<ref>{{cite web | url=https://concatenative.org/wiki/view/Concatenative%20language/Name%20code%20not%20values | title=Name code not values | publisher=Concatenative.org | accessdate=2020-10-16 | archive-date=2013-09-29 | archive-url=https://web.archive.org/web/20130929025044/http://concatenative.org/wiki/view/Concatenative%20language/Name%20code%20not%20values }}</ref>: <syntaxhighlight lang="python"> from functools import partial, reduce def compose(*func_list): return partial(reduce, lambda argv,func:func(argv), func_list) pipeline = compose(sort, uniq_c, sort_rn) c = pipeline(a) assert b == c </syntaxhighlight> ==jq语言== [[jq语言|jq]]是面向[[JSON]]的[[纯函数式编程]]语言,它使用<code>|</code>符号来连接过滤器形成流水线。例如: <syntaxhighlight lang="console"> $ echo '[1,2]' | jq 'add/length' 1.5 $ jq -n '[1,2] | add/length' 1.5 </syntaxhighlight> 这里jq内的JSON阵列<code>[1,2]</code>是求值为阵列的一个jq过滤器。尽管类似于Unix流水线,jq流水线允许将到来数据,如同并行的发送到在<code>|</code>右手端的多于一个接收者。 上述Python章节中例子,在jq中等价的无点风格定义为: <syntaxhighlight lang="python"> def uniq_c: map(tostring) | reduce .[] as $i ({}; .[$i] += 1) | to_entries | map([.value, .key]); def sort_nr: sort | reverse | map([.[0], (.[1]|tonumber)]); [2, 4, 3, 1, 3, 12, 2] | sort | uniq_c | sort_nr </syntaxhighlight> 将这段代码保存入<code>pipeline.jq</code>文件中,下面是其执行结果: <syntaxhighlight lang="console"> $ jq -nc -f ./pipeline.jq [[2,3],[2,2],[1,4],[1,12],[1,1]] </syntaxhighlight> ==函数式编程语言== 下面是采用[[纯函数式编程]]语言[[Haskell]]的一个简单例子,它在一个列表上作合计的函数。编程者可以使用“有点”(pointed)也称为[[值级编程]]的方法,而递归的定义这个合计为: <syntaxhighlight lang="haskell"> sum (x:xs) = x + sum xs sum [] = 0 </syntaxhighlight> 这是一种[[fold (高阶函数)|折叠]](fold)运算,编程者可以将它改写为: <syntaxhighlight lang="haskell"> sum xs = foldr (+) 0 xs </syntaxhighlight> 这里的参数是不必须的,进而将它改写成如下“无点”也称为[[函数级编程]]的样式: <syntaxhighlight lang="haskell"> sum = foldr (+) 0 </syntaxhighlight> Haskell拥有{{en-link|函数复合 (计算机科学)|Function composition (computer science)|函数复合}}[[算子 (编程)|算子]]: <syntaxhighlight lang="haskell"> (.) :: (b -> c) -> (a -> b) -> a -> c (.) f g = \x -> f (g x) </syntaxhighlight> 它有如下性质<ref>{{cite web|url=https://gist.github.com/cscalfani/30ff149a75fc5580d1f8aec61f8e5283|title=Functional Composition with Multiple Parameters in Haskell}}</ref>: <syntaxhighlight lang="haskell"> f . g = f(g) f . g = (.) f g f . g = (f .) g f . g = (. g) f </syntaxhighlight> 这里的<code>(f .)</code>和<code>(. g)</code>,分别称为<code>.</code>的左“节”(section)和右“节”,是对这个中缀算子的左侧和右侧{{en-link|部份应用|Partial application}}。<code>(f .) = ((.) f)</code>,本文采用后者形式。正如前面所说,在“无点”中的[[点]](point)指称参数,而非不使用[[乘法|点号]](dot),这是常见误解<ref>{{Cite web|url=https://wiki.haskell.org/Pointfree#But_pointfree_has_more_points.21|title=Pointfree - HaskellWiki|website=wiki.haskell.org|access-date=2016-06-05|archive-date=2021-04-28|archive-url=https://web.archive.org/web/20210428224553/https://wiki.haskell.org/Pointfree#But_pointfree_has_more_points.21}}</ref>。 下面的例子展示其用法,给出一个函数<code>p</code>的定义: <syntaxhighlight lang="haskell"> p x y z = f (g x y) z </syntaxhighlight> 经过如下推导: <syntaxhighlight lang="haskell"> p = \x -> \y -> \z -> f (g x y) z = \x -> \y -> \z -> (f ((g x) y)) z = \x -> \y -> f ((g x) y) = \x -> \y -> (f . (g x)) y = \x -> f . (g x) = \x -> ((.) f) (g x) = \x -> (((.) f) . g) x = ((.) f) . g </syntaxhighlight> 它可以归约成无点的等价定义: <syntaxhighlight lang="haskell"> p = ((.) f) . g </syntaxhighlight> 下面是一个复杂一些的例子<ref>{{Cite web |url=http://www.haskell.org/pipermail/haskell-cafe/2006-September/017758.html |title=pipermail |access-date=2020-04-18 |archive-date=2012-02-18 |archive-url=https://web.archive.org/web/20120218200826/http://www.haskell.org/pipermail/haskell-cafe/2006-September/017758.html }}</ref>,这里的<code>mf</code>是一个先做[[map (高阶函数)|映射]](map)再加[[filter (高阶函数)|过滤器]](filter)的函数,它接受一个列表<code>list</code>,向它应用一个函数<code>operator</code>,接着基于一个准则<code>criteria</code>来过滤元素: <syntaxhighlight lang="haskell"> mf criteria operator list = filter criteria (map operator list) </syntaxhighlight> 经过如下推导: <syntaxhighlight lang="haskell"> mf = \x -> \y -> \z -> filter x (map y z) = \x -> \y -> \z -> (filter x) ((map y) z) = \x -> \y -> \z -> (filter x) . (map y) z = \x -> \y -> (filter x) . (map y) = \x -> \y -> ((.) (filter x)) (map y) = \x -> \y -> ((.) (filter x)) . map y = \x -> ((.) (filter x)) . map = \x -> (. map) ((.) (filter x)) = \x -> (. map) ((.) . filter x) = \x -> (. map) . ((.) . filter) x = (. map) . ((.) . filter) = (. map) . (.) . filter </syntaxhighlight> <code>mf</code>可以表达为无点样式: <syntaxhighlight lang="haskell"> mf = (. map) . (.) . filter </syntaxhighlight> ==串接式编程语言== 在[[串接式编程语言]]和[[面向堆栈编程]]语言中,无点方法也很常用。例如,计算[[斐波那契数]]的过程,在[[Unix]]的[[dc (程序)|dc]]命令中可实现为: <syntaxhighlight> [d1<G]sF [d 1- lFx r 2- lFx +]sG lFx </syntaxhighlight> 下面是其用例: <syntaxhighlight lang="console"> $ echo 7 | dc -e '? [d1<G]sF [d 1- lFx r 2- lFx +]sG lFx p' 13 </syntaxhighlight> 在[[Factor语言|Factor]]中可与其等价的实现为<ref>{{cite web|url=https://rosettacode.org/wiki/Fibonacci_sequence#Factor|title=Rosetta Code — Fibonacci sequence}}</ref>: <syntaxhighlight lang="factor"> : fib ( n -- Fn ) dup 1 > [ [ 1 - fib ] [ 2 - fib ] bi + ] when ; </syntaxhighlight> 在这两个例子中,递归调用函数的参数在堆栈中而没有被显式的命名。 ==参见== * [[组合子逻辑]] * [[串接式编程语言]] * [[函数级编程]] ==注释和引用== {{Reflist|2}} ==外部链接== * [https://function-level.github.io/ From Function-Level Programming to Pointfree Style] * [http://portal.acm.org/citation.cfm?id=114065&dl=GUIDE&coll=GUIDE Pure Functions in APL and J] How to use tacit programming in any APL-like language * [http://dirkgerrits.com/publications/john-backus.pdf#section.8 Closed applicative languages 1971 - 1976 ff] {{Wayback|url=http://dirkgerrits.com/publications/john-backus.pdf#section.8 |date=20200207055523 }}, in John W. Backus (Publications) {{编程范型}} [[Category:编程范式]]
该页面使用的模板:
Template:Cite web
(
查看源代码
)
Template:En-link
(
查看源代码
)
Template:Main
(
查看源代码
)
Template:NoteTA
(
查看源代码
)
Template:Reflist
(
查看源代码
)
Template:Wayback
(
查看源代码
)
Template:编程范型
(
查看源代码
)
返回
隐式编程
。
导航菜单
个人工具
登录
命名空间
页面
讨论
不转换
查看
阅读
查看源代码
查看历史
更多
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
工具
链入页面
相关更改
页面信息