Это правильное использование сопоставления с образцом и активных рисунков? - PullRequest
1 голос
/ 07 ноября 2010

Я новичок в F # и функционирую и работаю над кодом для разбора HTML.Я хочу удалить из документа HTML элементы, соответствующие некоторым критериям.Здесь у меня есть последовательность объектов (HtmlNodes) и я хочу удалить их из документа.

Это идиоматический способ использования сопоставления с образцом?Кроме того, поскольку HtmlNode.Remove () имеет побочный эффект для исходного объекта HtmlDocument, существует ли какой-либо конкретный способ структурирования кода, чтобы сделать побочный эффект очевидным, или как его следует обрабатывать.Вы можете быть настолько педантичным, насколько захотите с кодом.

open HtmlAgilityPack

let removeNodes (node : HtmlNode) = 

    let (|HiddenNodeCount|) (n : HtmlNode) =
        match n.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with
        | null -> 0
        | _ as x -> Seq.length x

    match node with
        | x when x.Name.ToLower() = "script" -> node.Remove() 
        | x when x.NodeType = HtmlNodeType.Comment -> node.Remove()
        | HiddenNodeCount x when x > 0 -> node.Remove()
        | _ -> ()

let html = "some long messy html code would be here"
let dom = new HtmlDocument(OptionAutoCloseOnEnd=true)
dom.LoadHtml(html)

let nodes = dom.DocumentNode.DescendantNodes()
nodes |> Seq.toArray |> Array.iter removeNodes

Ответы [ 2 ]

1 голос
/ 07 ноября 2010

Лично я предпочитаю if elif else сопоставлению с шаблоном, когда у вас нет структуры данных для декомпозиции (она просто меньше печатает, а также может помочь различать, когда структура подвергается декомпозиции, и более простое тестирование случая).

В вашем коде есть странные вещи.Активный шаблон здесь не очень полезен по двум причинам: во-первых, его область действия ограничена removeNodes, поэтому он используется только один раз.Я расскажу о вторых проблемах позже, но сначала я покажу, как бы я это написал, исключив Active Pattern и, по крайней мере, для меня, сделав побочные эффекты более очевидными (выделив код, который проверяет, должен ли узел бытьудалено из кода, который выполняет удаление):

let shouldRemoveNode (node : HtmlNode) = 
    if node.Name.ToLower() = "script" then true
    elif node.NodeType = HtmlNodeType.Comment then true
    else match node.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with
         | null -> false
         | x -> Seq.length x > 0

let removeNode (node: HtmlNode) = 
    if shouldRemoveNode(node) then node.Remove() else ()

Обратите внимание, что я использую сопоставление с шаблоном в скрытом запросе видимости, поскольку я получаю сопоставление с нулем и связывание с х в противном случае (вместо привязки к хи затем проверяем x с помощью if else).

Вторая странная вещь в вашем активном шаблоне заключается в том, что вы используете его для преобразования узла в int, но полученная длина не сразу полезна (вам все еще нужно выполнить тест против него).Принимая во внимание, что более мощное использование Активного Паттерна здесь будет состоять в том, чтобы разделить узлы на различные виды (при условии, что это не случайный случай, который может быть первым пунктом).Таким образом, вы могли бы иметь:

//expand to encompass several other kinds of nodes
let (|Script|Comment|Hidden|Other|) (node : HtmlNode) = 
    if node.Name.ToLower() = "script" then Script
    elif node.NodeType = HtmlNodeType.Comment then Comment
    else match node.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with
         | null -> Other
         | x -> if Seq.length x > 0 then Hidden
                else Other

let removeNode (node: HtmlNode) = 
    match node with
    | Script | Comment | Hidden -> node.Remove()
    | Other -> ()

Редактировать:

@ Паскаль сделал замечание в комментариях, что shouldRemoveNode может быть далее сжато в одно большое логическое выражение:

let shouldRemoveNode (node : HtmlNode) = 
    node.Name.ToLower() = "script" || 
    node.NodeType = HtmlNodeType.Comment ||
    match node.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with
    | null -> false
    | x -> Seq.length x > 0
0 голосов
/ 07 ноября 2010

Мне не ясно, что это лучше, чем использовать функции и если-то-еще, например,

let HiddenNodeCount (n : HtmlNode) = 
    match n.SelectNodes("*[@style[contains(.,'visibility:hidden')]]") with 
    | null -> 0 
    | x -> Seq.length x 

if node.Name.ToLower() = "script" then 
    node.Remove()  
elif node.NodeType = HtmlNodeType.Comment then
    node.Remove()  
elif HiddenNodeCount node > 0 then 
    node.Remove()  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...