F# 不是解析/计算值,而是保存函数?因此,解析只发生一次
我正在尝试实现类似电子表格的功能,它维护变量,并在其他变量或“单元格”更改时正确更新它们 到目前为止,我看到的唯一一个实现是将输入的数据存储在字符串中并对其求值,但是当数据更改和单元格需要重新计算时,它总是需要再次对字符串求值 我只是想知道是否有可能将像Cell1=“SQUARED(4)”这样的字符串解析为Cell1=SQUARED 4 因此,它不需要将值计算到单元格中,而是需要在需要重新计算时反复运行字符串,而是可以将实际函数和值以某种方式保存在某个位置,这样解析和计算只需进行一次 如果这是不可能的,那么我将需要创建一个代码生成器,因为我需要非常快的速度,并且不能承受严重的性能损失。评估、编译等的速度并不重要。当输入数据改变数百万次并通过类似“电子表格”的系统传播时,重要的是所有单元格创建后的速度F# 不是解析/计算值,而是保存函数?因此,解析只发生一次,f#,F#,我正在尝试实现类似电子表格的功能,它维护变量,并在其他变量或“单元格”更改时正确更新它们 到目前为止,我看到的唯一一个实现是将输入的数据存储在字符串中并对其求值,但是当数据更改和单元格需要重新计算时,它总是需要再次对字符串求值 我只是想知道是否有可能将像Cell1=“SQUARED(4)”这样的字符串解析为Cell1=SQUARED 4 因此,它不需要将值计算到单元格中,而是需要在需要重新计算时反复运行字符串,而是可以将实际函数和值以某种方式保存在某个位置,这样解析和计算只需进行一次 如果这是不
所以这首先只是一个是或不是的问题。如果可能的话,任何例子当然都会有帮助。编辑:嗯,我想这会很有帮助,因为我自己也不知道这个问题。唐·赛姆关于备忘录的博客中有一个很好的例子- 你写这个函数
let memoize f =
let cache = Dictionary<_, _>()
fun x ->
if cache.ContainsKey(x) then cache.[x]
else let res = f x
cache.[x] <- res
res
如果您对实现电子表格之类的东西感兴趣,那么有两件事可能会有所帮助
- 首先,是F#中电子表格的示例实现。它包括一个简单的字符串解析器,比如“=SUM(A1:A10)”,并从这些字符串构建一个表达式树(这只需完成一次)。其次,它还包括一个计算表达式值的计算器
- 其次,Luca Bolognese讨论了称为“Eden”的计算框架的实现,在该框架中,您用单元格来描述计算,当单元格中的值发生变化时,变化会自动传播(并且只重新计算相关单元格)。他将在会议上发言,但我认为这已经被记录在某处(但找不到)
type Cell<'T> =
abstract Value : 'T
abstract Changed : IEvent<unit>
然后,您还可以构造作为某些计算结果生成的单元。这是一个演讲或博客文章的主题,不是一个简单的答案,但将单元格中的值映射到另一个单元格的简单转换如下所示:
let map f (cell:Cell<_>) =
let currentValue = ref (f cell.Value)
cell.Changed.Add(fun () -> currentValue := f cell.Value)
{ new Cell<_> with
member x.Value = currentValue.Value
member x.Changed = cell.Changed }
let映射f(单元格:单元格)=
让currentValue=ref(f cell.Value)
cell.Changed.Add(fun()->currentValue:=f cell.Value)
{带有
成员x.Value=当前值.Value
成员x.Changed=cell.Changed}
您需要能够组合来自多个单元格等的值。哇,这太快了!如果这真的是答案,那么哇,谢谢你!我可能需要一些时间才能更好地理解这一点,因此我将很快报告:)为了确保我们的意见一致,我不会一次又一次地计算所有单元格,只有当它们需要更新时,才会通知它们,并且现在重新计算字符串,但我希望它只是再次运行其函数。这有帮助吗因为我一直在读维基百科,我不确定这是我需要的。@user1594138这个想法是说你有:
let evaluate cell=..
你写let m_evaluate=memoize evaluate
,然后如果你重复输入调用m_evaluate
,你可以从缓存中获取一个值,而不必重新评估你的函数。是的,但是只有在需要更新的情况下才会更新,这意味着某些东西已经改变了。所以,也许我在我的帖子中给出了一个不好的例子,但大多数单元格都是基于其他单元格的,只有当通知其他单元格发生了变化时,才会更新此单元格所依赖的单元格。所以Cell1=cell2+cell3,Cell1仅在cell2或cell3发生变化时更新。因此,虽然它是同一个字符串cell1=“cell2+cell3”,但数据发生了变化。我只是想让它保存函数而不是字符串,所以Cell1=cell2+cell3,就像你用纯代码编写它一样。你至少可以保存部分工作-只有两个阶段-第一阶段从string->'t
开始,其中't
是某种计算速度相当快的数据类型-也可以存储依赖项,但你必须重复计算fast函数。不过,我建议你还是先试试天真版吧——在现代硬件上,它可能足够快。谢谢你的帮助,但我认为我的问题写得不够好,因为我知道所有这些。我已经看过cellz的源代码,并且非常了解它,这正是我想问的那种风格,是否可以不这样做。它的问题正是我键入的内容,它调用整个计算/解析函数,以及每次需要重新计算单元格时的所有开销。我要问的是,这是否可以跳过,解析器只是将值设置为如何在实际代码中编写它。所以cell1=cell2+1,而不是cell1=计算“cell2+1”,所以它只是以某种方式计算cell1等于cell1=cell2+1。看起来这是不可能的,我只需要创建一个代码生成器。因此,我只需输入Cell1=cell2+1,它将以普通代码创建对象、单元格条目、依赖项等,除了在代码生成器中生成代码外,不进行任何计算。抱歉,尽管这可能是一个非常愚蠢的问题,但我不知道什么是可能的,什么是不可能的。我想我会简单地创建一个代码生成器,让事情尽可能地高效,谢谢你的帮助!FSharp
type MutableCell<'T>(value:'T) =
let mutable currentValue = value
let event = Event<unit>()
member x.Value
with get() = currentValue
and set(v) =
currentValue <- v
event.Trigger()
interface Cell<'T> with
member x.Value = currentValue
member x.Changed = event.Publish
let map f (cell:Cell<_>) =
let currentValue = ref (f cell.Value)
cell.Changed.Add(fun () -> currentValue := f cell.Value)
{ new Cell<_> with
member x.Value = currentValue.Value
member x.Changed = cell.Changed }