Справка в разработке древовидной структуры - Сила между функциональным и ООП

От моего воспоминания большинство инструментов ORM отобразит его на Десятичный тип, который не потеряет точность как двойное или плавание.

5
задан Francesco 22 August 2009 в 11:45
поделиться

6 ответов

Когда дело доходит до «обновления дерева», я думаю, вы всегда можете сделать это довольно элегантно. используя катаморфизмы (складки над деревьями). У меня есть длинная серия блогов об этом, и большая часть приведенного ниже примера кода взята из части 4 серии .

При первом обучении я считаю, что лучше сосредоточиться на конкретном небольшом конкретном постановка задачи. Основываясь на вашем описании, я придумал следующую задачу:

У вас есть двоичное дерево, в котором каждый узел содержит «имя» и «количество» (может думайте об этом как о банковских счетах или о чем-то таком). И я хочу написать функцию который может сказать кому-то «украсть» определенную сумму у каждого из его прямых дети. Вот изображение, чтобы описать то, что я имею в виду:

альтернативный текст http://oljksa.bay.livefilestore.com/y1pNWjpCPP6MbI3rMfutskkTveCWVEns5xXaOf-NZlIz2Hs_CowykUmwtlVV7bP12MwEhmaxnGuwFreeGWGWhWhjs_s_s_s_c_s_s_CowykUmwtlVV7bPXRwE4Whjsg В среднем примере показан результат, который я хочу, если узел «D» просят украсть «10» у каждого из его детей. И правильный пример показывает желаемый результат, если вместо этого я попросил «F» украсть «30» в исходном примере.

Обратите внимание, что используемая мной древовидная структура будет неизменной, а красные цвета в Диаграмма обозначает «новые узлы дерева» относительно исходного дерева. Это черный узлы используются совместно с исходной древовидной структурой (Object.ReferenceEquals to one другой).

Теперь, предполагая типичную древовидную структуру, подобную

type Tree<'T> =                          //'
    | Node of 'T * Tree<'T> * Tree<'T>   //'
    | Leaf

, мы бы представили исходное дерево как

let origTree = Node(("D",1000),
                   Node(("B",1000),
                       Node(("A",1000),Leaf,Leaf),
                       Node(("C",1000),Leaf,Leaf)),
                   Node(("F",1000),
                       Node(("E",1000),Leaf,Leaf),
                       Leaf))

, а функцию «Steal» действительно легко написать, если у вас есть обычная «складка» шаблон:

// have 'stealerName' take 'amount' from each of its children and
// add it to its own value
let Steal stealerName amount tree =
    let Subtract amount = function
        | Node((name,value),l,r) -> amount, Node((name,value-amount),l,r)
        | Leaf -> 0, Leaf
    tree |> XFoldTree 
        (fun (name,value) left right ->
            if name = stealerName then
                let leftAmt, newLeft = Subtract amount left
                let rightAmt, newRight = Subtract amount right
                XNode((name,value+leftAmt+rightAmt),newLeft,newRight)
            else
                XNode((name,value), left, right))
        XLeaf
// examples
let dSteals10 = Steal "D" 10 origTree
let fSteals30 = Steal "F" 30 origTree

Вот и все, готово, вы написали алгоритм, который "обновляет" уровни L и L + 1 неизменяемого дерева, просто создав основную логику. Вместо того, чтобы объяснять все это здесь, вы должны пойти прочитать мою серию блогов (по крайней мере, начало: части один два три четыре ).

Вот весь код (который нарисовал рисунок выше):

// Tree boilerplate
// See http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!248.entry
type Tree<'T> =
    | Node of 'T * Tree<'T> * Tree<'T>
    | Leaf
let (===) x y = obj.ReferenceEquals(x,y)    
let XFoldTree nodeF leafV tree =  
    let rec Loop t cont =  
        match t with  
        | Node(x,left,right) -> Loop left  (fun lacc ->   
                                Loop right (fun racc ->  
                                cont (nodeF x lacc racc t)))
        | Leaf -> cont (leafV t)
    Loop tree (fun x -> x)
let XNode (x,l,r) (Node(xo,lo,ro) as orig) = 
    if xo = x && lo === l && ro === r then  
        orig 
    else 
        Node(x,l,r) 
let XLeaf (Leaf as orig) = 
    orig
let FoldTree nodeF leafV tree =  
    XFoldTree (fun x l r _ -> nodeF x l r) (fun _ -> leafV) tree
// /////////////////////////////////////////
// stuff specific to this problem
let origTree = Node(("D",1000),
                   Node(("B",1000),
                       Node(("A",1000),Leaf,Leaf),
                       Node(("C",1000),Leaf,Leaf)),
                   Node(("F",1000),
                       Node(("E",1000),Leaf,Leaf),
                       Leaf))

// have 'stealerName' take 'amount' from each of its children and
// add it to its own value
let Steal stealerName amount tree =
    let Subtract amount = function
        | Node((name,value),l,r) -> amount, Node((name,value-amount),l,r)
        | Leaf -> 0, Leaf
    tree |> XFoldTree 
        (fun (name,value) left right ->
            if name = stealerName then
                let leftAmt, newLeft = Subtract amount left
                let rightAmt, newRight = Subtract amount right
                XNode((name,value+leftAmt+rightAmt),newLeft,newRight)
            else
                XNode((name,value), left, right))
        XLeaf
let dSteals10 = Steal "D" 10 origTree
let fSteals30 = Steal "F" 30 origTree

// /////////////////////////////////////////
// once again,
// see http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!248.entry

// DiffTree: Tree<'T> * Tree<'T> -> Tree<'T * bool> 
// return second tree with extra bool 
// the bool signifies whether the Node "ReferenceEquals" the first tree 
let rec DiffTree(tree,tree2) = 
    XFoldTree (fun x l r t t2 ->  
        let (Node(x2,l2,r2)) = t2 
        Node((x2,t===t2), l l2, r r2)) (fun _ _ -> Leaf) tree tree2 

open System.Windows 
open System.Windows.Controls 
open System.Windows.Input 
open System.Windows.Media 
open System.Windows.Shapes 

// Handy functions to make multiple transforms be a more fluent interface 
let IdentT() = new TransformGroup() 
let AddT t (tg : TransformGroup) = tg.Children.Add(t); tg 
let ScaleT x y (tg : TransformGroup) = tg.Children.Add(new ScaleTransform(x, y)); tg 
let TranslateT x y (tg : TransformGroup) = tg.Children.Add(new TranslateTransform(x, y)); tg 

// Draw: Canvas -> Tree<'T * bool> -> unit 
let Draw (canvas : Canvas) tree = 
    // assumes canvas is normalized to 1.0 x 1.0 
    FoldTree (fun ((name,value),b) l r trans -> 
        // current node in top half, centered left-to-right 
        let tb = new TextBox(Width=100.0, Height=100.0, FontSize=30.0, Text=sprintf "%s:%d" name value, 
                             // the tree is a "diff tree" where the bool represents 
                             // "ReferenceEquals" differences, so color diffs Red 
                             Foreground=(if b then Brushes.Black else Brushes.Red),  
                             HorizontalContentAlignment=HorizontalAlignment.Center, 
                             VerticalContentAlignment=VerticalAlignment.Center) 
        tb.RenderTransform <- IdentT() |> ScaleT 0.005 0.005 |> TranslateT 0.25 0.0 |> AddT trans 
        canvas.Children.Add(tb) |> ignore 
        // left child in bottom-left quadrant 
        l (IdentT() |> ScaleT 0.5 0.5 |> TranslateT 0.0 0.5 |> AddT trans) 
        // right child in bottom-right quadrant 
        r (IdentT() |> ScaleT 0.5 0.5 |> TranslateT 0.5 0.5 |> AddT trans) 
    ) (fun _ -> ()) tree (IdentT()) 

let TreeToCanvas tree =
    let canvas = new Canvas(Width=1.0, Height=1.0, Background = Brushes.Blue, 
                            LayoutTransform=new ScaleTransform(400.0, 400.0)) 
    Draw canvas tree
    canvas

let TitledControl title control =
    let grid = new Grid()
    grid.ColumnDefinitions.Add(new ColumnDefinition())
    grid.RowDefinitions.Add(new RowDefinition())
    grid.RowDefinitions.Add(new RowDefinition())
    let text = new TextBlock(Text = title, HorizontalAlignment = HorizontalAlignment.Center)
    Grid.SetRow(text, 0)
    Grid.SetColumn(text, 0)
    grid.Children.Add(text) |> ignore
    Grid.SetRow(control, 1)
    Grid.SetColumn(control, 0)
    grid.Children.Add(control) |> ignore
    grid

let HorizontalGrid (controls:_[]) =
    let grid = new Grid()
    grid.RowDefinitions.Add(new RowDefinition())
    for i in 0..controls.Length-1 do
        let c = controls.[i]
        grid.ColumnDefinitions.Add(new ColumnDefinition())
        Grid.SetRow(c, 0)
        Grid.SetColumn(c, i)
        grid.Children.Add(c) |> ignore
    grid

type MyWPFWindow(content, title) as this = 
    inherit Window()

    do  
        this.Content <- content
        this.Title <- title
        this.SizeToContent <- SizeToContent.WidthAndHeight  

[<System.STAThread()>] 
do  
    let app =  new Application() 
    let controls = [|
        TitledControl "Original" (TreeToCanvas(DiffTree(origTree,origTree)))
        TitledControl "D steals 10" (TreeToCanvas(DiffTree(origTree,dSteals10)))
        TitledControl "F steals 30" (TreeToCanvas(DiffTree(origTree,fSteals30))) |]
    app.Run(new MyWPFWindow(HorizontalGrid controls, "Fun with trees")) |> ignore 
6
ответ дан 18 December 2019 в 11:59
поделиться

Думаю, если вы начнете свое предложение со слов «Я должен изменить узлы дерева, изменив одновременно с утверждает на двух разных уровнях, «тогда вы на самом деле не решаете свою проблему функциональным способом. Это все равно, что написать статью на иностранном языке, сначала написав ее на своем родном языке, а затем пытаясь перевести. На самом деле не работает. Знаю, это больно, но на мой взгляд, лучше полностью погрузиться. Пока не беспокойтесь о сравнении подходов.

Один из способов, который я нашел для изучения «функционального способа», - это посмотреть (и реализовать себя!) Некоторые функциональные жемчужины . Они в основном хорошо документируют сверхфункциональные элегантные программы для решения множества проблем. Начните со старых и не бойтесь бросать читать и попробуйте другой, если не понимаете. Просто вернитесь к этому позже с новым энтузиазмом и большим опытом. Помогает :)

4
ответ дан 18 December 2019 в 11:59
поделиться

Я должен изменять узлы дерева.

Нет, не надо. Вот и твоя проблема.

Это стоило мне больших усилий

Это типично. Научиться программировать с неизменяемыми структурами данных непросто. И большинству новичков поначалу это кажется неестественным. Это вдвойне сложно, потому что HTDP и SICP не дают вам хороших моделей для подражания (см. Сноску).

Я подумал, что вы могли бы дать мне несколько советов, конечно, мое представление о «хорошем дизайне функционального программирования» еще не очень хорошо определено .

Мы можем, но вы должны сообщить нам, в чем проблема . Тогда многие люди на этом форуме могут сказать вам, относится ли это к той проблеме, решение которой можно четко сформулировать, не прибегая к мутации. Большинство проблем с деревьями могут. Но с информацией, которую вы нам предоставили, у нас нет возможности сказать.

Я слишком строго интерпретирую «никогда не изменяйте данные»?

Я бы сказал, что недостаточно строго.

Пожалуйста, отправьте сообщение. вопрос, указывающий, какую проблему вы пытаетесь решить.


Сноска: и HTDP, и SICP выполняются на схеме, в которой отсутствует сопоставление с образцом. В этой настройке намного сложнее понять код обработки дерева, чем при использовании сопоставления с образцом, предоставляемого F #. Насколько мне известно, сопоставление с образцом - важная функция для написания четкого кода в чисто функциональном стиле.

2
ответ дан 18 December 2019 в 11:59
поделиться

What do you think about this "tension" which I am experiencing? Am I interpreting the "never mutate data" too strictly? Could you suggest me some resource?

In my opinion, if you're learning functional programming for the first time, its best to start out with zero mutable state. Otherwise, you'll only end up falling back on mutable state as your first resort, and all of your F# code will be C# with a little different syntax.

Regarding data structures, some are easier to express in a functional style than others. Could you provide a description of how you're trying to modify your tree?

For now, I would recommend F# Wikibook's page on data structures to see how data structures are written in a functional style.

I've looked at SICP, at How to design programs and have found a thesis by C. Окасаки ("Чисто функциональные данные структур »)

Я лично нашел книгу Окасаки более читаемой, чем тезис в Интернете.

2
ответ дан 18 December 2019 в 11:59
поделиться

Взгляните на структуру данных Zipper .

1
ответ дан 18 December 2019 в 11:59
поделиться

Например, мне нужно изменить узлы дерева, изменив в то же время состояния на двух разных уровнях (L и L + 1)

Почему? На функциональном языке вместо этого вы должны создать новое дерево. Он может повторно использовать поддеревья, которые не нужно изменять, и просто вставить их во вновь созданный корень. «Не изменять данные» не означает «пытаться изменить данные так, чтобы никто не заметил этого, и добавив так много вспомогательных методов, что никто не поймет , что это то, что вы делаете»

. Это означает "не надо" t изменить ваши данные. Вместо этого создайте новые копии, которые инициализируются новыми правильными значениями ».

0
ответ дан 18 December 2019 в 11:59
поделиться