От моего воспоминания большинство инструментов ORM отобразит его на Десятичный тип, который не потеряет точность как двойное или плавание.
Когда дело доходит до «обновления дерева», я думаю, вы всегда можете сделать это довольно элегантно. используя катаморфизмы (складки над деревьями). У меня есть длинная серия блогов об этом, и большая часть приведенного ниже примера кода взята из части 4 серии .
При первом обучении я считаю, что лучше сосредоточиться на конкретном небольшом конкретном постановка задачи. Основываясь на вашем описании, я придумал следующую задачу:
У вас есть двоичное дерево, в котором каждый узел содержит «имя» и «количество» (может думайте об этом как о банковских счетах или о чем-то таком). И я хочу написать функцию который может сказать кому-то «украсть» определенную сумму у каждого из его прямых дети. Вот изображение, чтобы описать то, что я имею в виду:
Обратите внимание, что используемая мной древовидная структура будет неизменной, а красные цвета в Диаграмма обозначает «новые узлы дерева» относительно исходного дерева. Это черный узлы используются совместно с исходной древовидной структурой (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
Думаю, если вы начнете свое предложение со слов «Я должен изменить узлы дерева, изменив одновременно с утверждает на двух разных уровнях, «тогда вы на самом деле не решаете свою проблему функциональным способом. Это все равно, что написать статью на иностранном языке, сначала написав ее на своем родном языке, а затем пытаясь перевести. На самом деле не работает. Знаю, это больно, но на мой взгляд, лучше полностью погрузиться. Пока не беспокойтесь о сравнении подходов.
Один из способов, который я нашел для изучения «функционального способа», - это посмотреть (и реализовать себя!) Некоторые функциональные жемчужины . Они в основном хорошо документируют сверхфункциональные элегантные программы для решения множества проблем. Начните со старых и не бойтесь бросать читать и попробуйте другой, если не понимаете. Просто вернитесь к этому позже с новым энтузиазмом и большим опытом. Помогает :)
Я должен изменять узлы дерева.
Нет, не надо. Вот и твоя проблема.
Это стоило мне больших усилий
Это типично. Научиться программировать с неизменяемыми структурами данных непросто. И большинству новичков поначалу это кажется неестественным. Это вдвойне сложно, потому что HTDP и SICP не дают вам хороших моделей для подражания (см. Сноску).
Я подумал, что вы могли бы дать мне несколько советов, конечно, мое представление о «хорошем дизайне функционального программирования» еще не очень хорошо определено .
Мы можем, но вы должны сообщить нам, в чем проблема . Тогда многие люди на этом форуме могут сказать вам, относится ли это к той проблеме, решение которой можно четко сформулировать, не прибегая к мутации. Большинство проблем с деревьями могут. Но с информацией, которую вы нам предоставили, у нас нет возможности сказать.
Я слишком строго интерпретирую «никогда не изменяйте данные»?
Я бы сказал, что недостаточно строго.
Пожалуйста, отправьте сообщение. вопрос, указывающий, какую проблему вы пытаетесь решить.
Сноска: и HTDP, и SICP выполняются на схеме, в которой отсутствует сопоставление с образцом. В этой настройке намного сложнее понять код обработки дерева, чем при использовании сопоставления с образцом, предоставляемого F #. Насколько мне известно, сопоставление с образцом - важная функция для написания четкого кода в чисто функциональном стиле.
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. Окасаки ("Чисто функциональные данные структур »)
Я лично нашел книгу Окасаки более читаемой, чем тезис в Интернете.
Например, мне нужно изменить узлы дерева, изменив в то же время состояния на двух разных уровнях (L и L + 1)
Почему? На функциональном языке вместо этого вы должны создать новое дерево. Он может повторно использовать поддеревья, которые не нужно изменять, и просто вставить их во вновь созданный корень. «Не изменять данные» не означает «пытаться изменить данные так, чтобы никто не заметил этого, и добавив так много вспомогательных методов, что никто не поймет , что это то, что вы делаете»
. Это означает "не надо" t изменить ваши данные. Вместо этого создайте новые копии, которые инициализируются новыми правильными значениями ».