- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在代码审查中,我回答了一个关于 naive Haskell fizzbuzz solution 的问题。通过建议 iterates forward 的实现,避免了增加素数的二次成本和(几乎)完全丢弃模除法。这是代码:
fizz :: Int -> String
fizz = const "fizz"
buzz :: Int -> String
buzz = const "buzz"
fizzbuzz :: Int -> String
fizzbuzz = const "fizzbuzz"
fizzbuzzFuncs = cycle [show, show, fizz, show, buzz, fizz, show, show, fizz, buzz, show, fizz, show, show, fizzbuzz]
toFizzBuzz :: Int -> Int -> [String]
toFizzBuzz start count =
let offsetFuncs = drop (mod (start - 1) 15) fizzbuzzFuncs
in take count $ zipWith ($) offsetFuncs [start..]
Data.List.unfoldr
重写它。 .
unfoldr
version 是对这段代码的明显、简单的修改,所以我不会在这里输入它,除非寻求回答我的问题的人坚持认为这很重要(代码审查中的 OP 没有剧透)。但我确实对
unfoldr
的相对效率有疑问。与
zipWith
相比的解决方案一。虽然我不再是 Haskell 新手,但我也不是 Haskell 内部结构方面的专家。
unfoldr
解决方案不需要
[start..]
无限列表,因为它可以简单地从
start
展开.我的想法是
zipWith
解决方案不会记住 [start..]
的每个连续元素正如它所要求的那样。每个元素都被使用和丢弃,因为没有保留对 [start..] 头部的引用。所以没有比 unfoldr
更多的内存消耗。 . unfoldr
性能的担忧最近的补丁使其始终内联,这是在我尚未达到的水平上进行的。 unfoldr
用于生成序列似乎是一件很自然的事情,即使其他解决方案更具表现力。我只知道我需要更多地了解它的实际性能。 (出于某种原因,我发现
foldr
在该级别上更容易理解)
unfoldr
对
Maybe
的使用是我什至开始调查该问题之前发生的第一个潜在性能问题(也是我完全理解的优化/内联讨论的唯一一点)。所以我可以不用担心
Maybe
马上(鉴于 Haskell 的最新版本)。
最佳答案
作为负责 zipWith
的最近实现变化的人和 unfoldr
,我想我可能应该尝试一下。我不能那么容易地比较它们,因为它们是非常不同的功能,但我可以尝试解释它们的一些属性和变化的意义。unfoldr
内联
旧版unfoldr
(在 base-4.8
/GHC 7.10 之前)在顶层是递归的(它直接调用自身)。 GHC 从不内联递归函数,所以 unfoldr
从未内联。结果,GHC 无法看到它如何与传递的函数进行交互。最令人不安的影响是传入的函数类型为 (b -> Maybe (a, b))
。 , 实际上会产生 Maybe (a, b)
值,分配内存来保存 Just
和 (,)
构造函数。通过重组 unfoldr
作为“worker”和“wrapper”,新代码允许 GHC 内联它并(在许多情况下)将它与传入的函数融合,因此额外的构造函数被编译器优化剥离。
例如,在 GHC 7.10 下,代码
module Blob where
import Data.List
bloob :: Int -> [Int]
bloob k = unfoldr go 0 where
go n | n == k = Nothing
| otherwise = Just (n * 2, n+1)
ghc -O2 -ddump-simpl -dsuppress-all -dno-suppress-type-signatures
编译导致核心
$wbloob :: Int# -> [Int]
$wbloob =
\ (ww_sYv :: Int#) ->
letrec {
$wgo_sYr :: Int# -> [Int]
$wgo_sYr =
\ (ww1_sYp :: Int#) ->
case tagToEnum# (==# ww1_sYp ww_sYv) of _ {
False -> : (I# (*# ww1_sYp 2)) ($wgo_sYr (+# ww1_sYp 1));
True -> []
}; } in
$wgo_sYr 0
bloob :: Int -> [Int]
bloob =
\ (w_sYs :: Int) ->
case w_sYs of _ { I# ww1_sYv -> $wbloob ww1_sYv }
unfoldr
正在重写它以参与“折叠/构建”融合,这是 GHC 列表库中使用的优化框架。 “折叠/构建”融合和更新的、不同平衡的“流融合”(在
vector
库中使用)的想法是,如果列表由“好的生产者”生成,则由“好的转换器”转换,并被“好消费者”消费,那么列表 conses 根本不需要分配。老
unfoldr
不是一个好的制作人,所以如果你制作了一个带有
unfoldr
的列表并将其与
foldr
一起使用,随着计算的进行,列表的各个部分将被分配(并立即变成垃圾)。现在,
unfoldr
是一个很好的生产者,所以你可以写一个循环,比如说,
unfoldr
,
filter
, 和
foldr
,而不是(必然)分配任何内存。
bloob
的定义和严厉的
{-# INLINE bloob #-}
(这东西有点脆弱;好的生产者有时需要明确内联才能好),代码
hooby :: Int -> Int
hooby = sum . bloob
$whooby :: Int# -> Int#
$whooby =
\ (ww_s1oP :: Int#) ->
letrec {
$wgo_s1oL :: Int# -> Int# -> Int#
$wgo_s1oL =
\ (ww1_s1oC :: Int#) (ww2_s1oG :: Int#) ->
case tagToEnum# (==# ww1_s1oC ww_s1oP) of _ {
False -> $wgo_s1oL (+# ww1_s1oC 1) (+# ww2_s1oG (*# ww1_s1oC 2));
True -> ww2_s1oG
}; } in
$wgo_s1oL 0 0
hooby :: Int -> Int
hooby =
\ (w_s1oM :: Int) ->
case w_s1oM of _ { I# ww1_s1oP ->
case $whooby ww1_s1oP of ww2_s1oT { __DEFAULT -> I# ww2_s1oT }
}
Maybe
s,没有对;它执行的唯一分配是
Int
用于存储最终结果(
I#
到
ww2_s1oT
的应用)。可以合理地预期整个计算将在机器寄存器中执行。
zipWith
zipWith
有一个奇怪的故事。它有点笨拙地适合折叠/构建框架(我相信它在流融合方面工作得更好)。可以制作
zipWith
fuse 与它的第一个或第二个列表参数,并且多年来,列表库试图使其与其中任何一个融合,如果其中一个是一个好的生产者。不幸的是,使其与第二个列表参数融合可能会使程序在某些情况下的定义更少。即,使用
zipWith
的程序在没有优化的情况下编译时可以正常工作,但在使用优化编译时会产生错误。这不是一个很好的情况。因此,截至
base-4.8
,
zipWith
不再尝试与它的第二个列表参数融合。如果你想让它与一个好的生产者融合,那个好的生产者最好在第一个列表参数中。
zipWith
的引用实现导致期望,例如,
zipWith (+) [1,2,3] (1 : 2 : 3 : undefined)
会给
[2,4,6]
,因为它一到达第一个列表的末尾就停止了。与之前的
zipWith
实现,如果第二个列表看起来像这样,但由一个好的生产者制作,并且如果
zipWith
碰巧与它融合而不是第一个列表,然后它就会繁荣起来。
关于performance - 展开器与 zipWith 的效率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32521479/
第一个 .on 函数比第二个更有效吗? $( "div.container" ).on( "click", "p", function(){ }); $( "body" ).on( "click",
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 已关闭 7 年前。 Improve
我有这样的查询: $('#tabContainer li'); JetBrains WebStorm IDE 将其突出显示为低效查询。它建议我改用这个: $('#tabContainer').find
我刚刚在 coursera ( https://www.coursera.org/saas/) 上听了一个讲座,教授说 Ruby 中的一切都是对象,每个方法调用都是在对象上调用发送方法,将一些参数传递
这可能是用户“不喜欢”的另一个问题,因为它更多的是与建议相关而不是与问题相关。 我有一个在保存和工作簿打开时触发的代码。 它在 f(白天与夜晚,日期与实际日期)中选择正确的工作表。 周一到周三我的情况
这只是我的好奇心,但是更有效的是递归还是循环? 给定两个功能(使用通用lisp): (defun factorial_recursion (x) (if (> x 0) (*
这可能是一个愚蠢的问题,但是while循环的效率与for循环的效率相比如何?我一直被教导,如果可以使用for循环,那我应该这样做。但是,实际上之间的区别是什么: $i = 0; while($i <
我有一个Elasticsearch索引,其中包含几百万条记录。 (基于时间戳的日志记录) 我需要首先显示最新记录(即,按时间戳降序排列的记录) 在时间戳上排序desc是否比使用时间戳的函数计分功能更有
使用Point2D而不是double x和y值时,效率有很大差异吗? 我正在开发一个程序,该程序有许多圆圈在屏幕上移动。他们各自从一个点出发,并越来越接近目的地(最后,他们停下来)。 使用 .getC
我正在编写一个游戏,并且有一个名为 GameObject 的抽象类和三个扩展它的类(Player、Wall 和 Enemy)。 我有一个定义为包含游戏中所有对象的列表。 List objects; 当
我是 Backbone 的初学者,想知道两者中哪一个更有效以及预期的做事方式。 A 型:创建一个新集合,接受先前操作的结果并从新集合中提取 key result = new Backbone.Coll
最近,关于使用 LIKE 和通配符搜索 MS SQL 数据库的最有效方法存在争论。我们正在使用 %abc%、%abc 和 abc% 进行比较。有人说过,术语末尾应该始终有通配符 (abc%)。因此,根
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 8 年前。 Improv
我想知道,这样做会更有效率吗: setVisible(false) // if the component is invisible 或者像这样: if(isVisible()){
我有一个静态方法可以打开到 SQL Server 的连接、写入日志消息并关闭连接。我在整个代码中多次调用此方法(平均每 2 秒一次)。 问题是 - 它有效率吗?我想也许积累一些日志并用一个连接插入它们
这个问题在这里已经有了答案: Best practice to avoid memory or performance issues related to binding a large numbe
我为我的 CS 课(高中四年级)制作了一个石头剪刀布游戏,我的老师给我的 shell 文件指出我必须将 do while 循环放入运行者中,但我不明白为什么?我的代码可以工作,但她说最好把它写在运行者
我正在编写一个需要通用列表的 Java 应用程序。该列表需要能够经常动态地调整大小,对此的明显答案是通用的Linkedlist。不幸的是,它还需要像通过调用索引添加/删除值一样频繁地获取/设置值。 A
我的 Mysql 语句遇到了真正的问题,我需要将几个表连接在一起,查询它们并按另一个表中值的平均值进行排序。这就是我所拥有的... SELECT ROUND(avg(re.rating
这个问题在这里已经有了答案: 关闭 10 年前。 Possible Duplicate: Is there a difference between i==0 and 0==i? 以下编码风格有什么
我是一名优秀的程序员,十分优秀!