- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一种算法,可以根据边缘标记的输入列表删除从指定节点开始的图的不可达节点:
let g = Set.ofList [ (0, false, 1);
(0, true, 2);
(1, true, 3);
(1, false, 4);
(2, true, 4);
(4, false, 4); ]
let nextNode graph prev value = graph |> Seq.tryPick (function
| prev', value', next when prev = prev' && value = value' -> Some next
| _ -> None)
let noIncoming graph node =
not <| Set.exists (fun (_, _, node') -> node = node') graph
let clearEdges graph start input =
let graph' = ref graph
let current = ref (Some start)
input
|> Seq.takeWhile (fun _ -> Option.exists (noIncoming !graph') !current)
|> Seq.iter (fun value ->
let next = nextNode !graph' (!current).Value value
graph' := Set.filter (fun (current', _, _) -> (!current).Value <> current') !graph'
current := next)
graph'
clearEdges g 0 [false; true]
> val it : Set<int * bool * int> ref =
{contents = set [(2, true, 4); (4, false, 4)];}
它有效,但据我所知,我怀疑带有引用的算法 clearEdges
很丑陋,而且不是 F# 风格的。我尝试过以函数方式编写它,但可能我已经收到了迭代算法和收集方法的混合。有没有任何函数式方法可以做到这一点?因为我认为丑陋的工作代码比没有代码更糟糕。谢谢。
最佳答案
正如其他人在答案和评论中所说,回答这个问题最困难的部分是理解代码。它缺乏良好的描述和评论。
为了理解代码,我做的第一件事就是添加类型签名,然后将 printfn
语句添加到代码中,以查看它在做什么。
之后,因为更容易理解问题中的代码。
在重新设计代码时,我并没有尝试一次更改一小部分,而是根据我从 printfn
输出和类型签名中学到的知识从头开始构建核心功能。我毫不犹豫地从使用 ref
的可变代码切换到在每个函数中从头开始重建的不可变图。扔掉现有的数据结构并每次构建一个新的数据结构似乎是一种浪费,但请这样考虑:必须在每个边缘上做出决策的函数必须访问每个边缘,因此当您访问每个边缘时您可以将其添加到新图表中,也可以不添加,这使得编码变得更加容易,并且对于其他试图理解它的人来说也更加容易。
我还添加了类型,以使类型签名更有意义,并让代码的功能更加清晰。一点点工作就能得到巨大的奖励。
然后,我查看了这些函数,并没有专注于使代码尽可能简洁,而是专注于可读性和可维护性,并分解了一个函数以使其更加明显。
显然这个答案比其他两个更长,但比原始代码更实用,没有可变的,第一次阅读时更容易理解,并注释了每个函数的作用。
如果这是库的一部分,则应修改代码以删除类型签名,如果这是通用的,则不可以选择。还要将单独的函数设为内部函数,并重构其中的一些函数以利用内置的 F# 核心函数,并添加更多注释以弥补这样做时的清晰度损失。
在早期版本中,我使用 List.pick但意识到它可能会抛出 KeyNotFoundException
异常,因为我喜欢我的函数是 total如果可能的话对其进行修改以避免异常。
在查看我的答案时,我对if not (nodeUsed graph node) then 不满意
;这是简单中的一个疣。所以我决定求助于函数式编程这把古老的瑞士军刀:factoring 。 Pure functional编程基本上是可以像数学表达式一样分解的表达式,或者更理论上 term rewriting 。我知道如果我能用 not
分解出这条线,我就能让它变得更漂亮,也更容易理解。因此,分解 not
的方法是将其移到 let rec
之外,例如pathToNodes
,这可以通过传入节点列表而不是转换列表来完成,例如reduceGraph2
。一旦完成,代码就变得简单了。
我确信人们可以进一步分解代码,但我通常会将这样的答案留给 F# 新人,因为它们更容易理解。
namespace Workspace
module main =
type Node = int
type Transition = bool
type Edge = Node * Transition * Node
type Path = Transition list
type Graph = Edge list
[<EntryPoint>]
let main argv =
let edge1 : Edge = (0, false, 1)
let edge2 : Edge = (0, true , 2)
let edge3 : Edge = (1, true , 3)
let edge4 : Edge = (1, false, 4)
let edge5 : Edge = (2, true , 4)
let edge6 : Edge = (4, false, 4)
let g : Graph = [edge1; edge2; edge3; edge4; edge5; edge6]
// Given a Node, are there any Edges to that node
let nodeUsed (graph : Graph) (checkNode : Node) : bool =
List.exists (fun (_, _, toNode) -> checkNode = toNode) graph
// Given a Node and a transition, what is the next node
// This assumes that Transition is binary
// so only a value will be returned instead of a list.
let nextNode (graph : Graph) (fromNode : Node) (transition : Transition) : Node option =
let rec pick (graph : Graph) (fromNode : Node) (transition : Transition) : Node option =
match graph with
| (f, c, t)::tl when (f = fromNode) && (c = transition) -> Some t
| hd::tl -> pick tl fromNode transition
| _ -> None
pick graph fromNode transition
// Given a graph and a node, remove all edges that start from that node.
// This builds a new graph each time, thus the graph is immutable.
let removeNode (graph : Graph) (node : Node) : Graph =
let rec removeEdges graph node newGraph =
match graph with
| hd::tl ->
let (f,c,t) = hd
if (f = node)
// Don't add current node to new graph
then removeEdges tl node newGraph
// Add current node to new graph
else removeEdges tl node ((f,c,t) :: newGraph)
| [] -> newGraph
removeEdges graph node []
// or
// let removeNode (graph : Graph) (node : Node) : Graph =
// let choiceFunction elem =
// match elem with
// | (f,c,t) when f = node -> None
// | _ -> Some(elem)
// List.choose choiceFunction graph
// Given a graph, a starting node, and a list of transitions (path),
// return a new reduced graph based on the example code in the SO question.
let reduceGraph (graph : Graph) (start : Node) (path : Path) : Graph =
let rec processTransistion graph node transitions =
if not (nodeUsed graph node) then
match transitions with
| (transistion :: transitions) ->
// Get next node before removing nodes used to get next node
let nextNodeResult = nextNode graph node transistion
match nextNodeResult with
| Some(nextNode) ->
let newGraph = removeNode graph node
processTransistion newGraph nextNode transitions
| None -> graph
| [] -> graph
else graph
processTransistion graph start path
let result = reduceGraph g 0 [false; true]
printfn "reduceGraph - result: %A" result
printf "Press any key to exit: "
System.Console.ReadKey() |> ignore
printfn ""
0 // return an integer exit code
.
// Give an graph, a node and a path,
// convert the transition list (path) to a node list
let pathToNodes (graph : Graph) (start : Node) (path : Path) : (Node List) =
let rec visit graph node transistions acc =
match transistions with
| (transition::rest) ->
match (nextNode graph node transition) with
| Some(nextNode) -> visit graph nextNode rest (nextNode :: acc)
| None -> List.rev acc
| [] -> List.rev acc
visit graph start path [start]
// Given a graph, a starting node, and a list of transitions (path),
// return a new reduced graph based on the example code in the SO question.
// This variation does so internally by a node list instead of a transition list
let reduceGraph2 (graph : Graph) (start : Node) (path : Path) : Graph =
let rec processNodes graph nodes =
match nodes with
| (currentNode :: rest) -> processNodes (removeNode graph currentNode) rest
| [] -> graph
let nodes = pathToNodes graph start path
processNodes graph nodes
关于recursion - 如何在功能上编写针对具有依赖条件的动态更改集合的迭代算法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37520212/
如果您有超过 1 个具有相同类名的(动态)文本框,并使用 jquery 循环遍历每个所述文本框,您是否可以假设每次选择文本框的顺序都是相同的? 示例: 文本框 1 值 = 1文本框 2 值 = 2文本
有人知道为什么这段代码无法顺利运行吗?它似乎不喜欢使用yield关键字进行迭代:我正在尝试从任何级别的列表或字典中挖掘所有数字(对列表特别感兴趣)。在第二次迭代中,它找到 [2,3] 但无法依次打印
我关于从 mysql 数据库导出数据并将其保存到 Excel 文件(多表)的创建脚本。我需要让细胞动态基因化。该脚本正确地显示了标题,但数据集为空。当我“回显”$value 变量时,我检查了数据是否存
我正在尝试在 Python 中运行模拟,由此我绘制了一个数组的随机游走图,给定了两个变量参数的设定水平。 但是,我遇到了一个问题,我不确定如何迭代以便生成 250 个不同的随机数以插入公式。例如我已经
我是学习 jquery 的新手,所以如果这是一个相对简单的问题,我深表歉意。我有一个 ID 为 ChartstoDisplay 的 asp.net 复选框列表。我正在尝试创建 jquery 来根据是否
我正在尝试根据在任意数量的部分中所做的选择找出生成有效案例列表的最佳方法。也许它不是真正的算法,而只是关于如何有效迭代的建议,但对我来说这似乎是一个算法问题。如果我错了,请纠正我。实现实际上是在 Ja
如果我使用 sr1 为 www.google.com 发送 DNSQR,我会收到几个 DNSRR(s) 作为回复,例如(使用 ans[DNSRR].show() 完成): ###[ DNS Resou
假设有这样一个实体类 @Entity public class User { ... public Collection followers; ... } 假设用户有成千上万的用户关注者。我想分页..
这个问题已经有答案了: 已关闭11 年前。 Possible Duplicate: Nested jQuery.each() - continue/break 这是我的代码: var steps =
我刚从 F# 开始,我想遍历字典,获取键和值。 所以在 C# 中,我会说: IDictionary resultSet = test.GetResults; foreach (DictionaryEn
我知道已经有很多关于如何迭代 ifstream 的答案,但没有一个真正帮助我找到解决方案。 我的问题是:我有一个包含多行数据的txt文件。 txt 文件的第一行告诉我其余数据是如何组成的。例如这是我的
我有 12 个情态动词。我想将每个模态的 .modal__content 高度与 viewport 高度 进行比较,并且如果特定模态 .modal__content 高度 vh addClass("c
在此JSFiddle (问题代码被注释掉)第一次单击空单元格会在隐藏输入中设置一个值,并将单元格的背景颜色设置为绿色。单击第二个空表格单元格会设置另一个隐藏输入的值,并将第二个单元格的背景颜色更改为红
这是一个非常具体的问题,我似乎找不到任何特别有帮助的内容。我有一个单链表(不是一个实现的链表,这是我能找到的全部),其中节点存储一个 Student 对象。每个 Student 对象都有变量,尽管我在
有没有办法迭代 IHTMLElementCollection? 比如 var e : IHTMLLinkElement; elementCollection:IHTMLElementCollect
我正在尝试用 Java 取得高分。基本上我想要一个 HashMap 来保存 double 值(因此索引从最高的 double 值开始,这样我更容易对高分进行排序),然后第二个值将是客户端对象,如下所示
我想在宏函数中运行 while/until 循环,并限制其最大迭代次数。我找到了如何在“通常”sas 中执行此操作: data dataset; do i=1 to 10 until(con
Iterator iterator = plugin.inreview.keySet().iterator(); while (iterator.hasNext()) { Player key
晚上好我有一个简单的问题,我警告你我是序言的新手。假设有三个相同大小的列表,每个列表仅包含 1、0 或 -1。我想验证对于所有 i,在三个列表的第 i 个元素中,只有一个非零。 此代码针对固定的 i
我在 scheme 中构建了一个递归函数,它将在某些输入上重复给定函数 f, n 次。 (define (recursive-repeated f n) (cond ((zero? n) iden
我是一名优秀的程序员,十分优秀!