2016-10-05 56 views
6

Çocukken başka bir metin düğümüne sahip olabilecek metin düğümleri gibi bir ağaç yapısına sahibim ve bir değeri güncellemem gerekiyor. Bu ağacın derinliklerinde bir yer olan metin düğümünü güncellemenin en kolay yolu nedir (ya da o ağacın içinde değil)?Özyineleme türünde güncelleme değeri - elm lang

Değişken olmayan bir dilde, sadece bu öğenin değerini değiştirirdim, ve bu kadar, ama Elm gibi değişmez bir dilde oldukça zor.

type alias Item = 
    { id: String 
    , text: String 
    , children: ChildItems 
    } 

type ChildItems = ChildItems (List Item) 

type alias Model = 
    { rootItem: Item 
    } 

updateItem: Item -> Item -> Item 
updateItem: rootItem item = 
    -- TODO 

... 

update model = 
    case msg of 
    UpdateItem item updatedText -> 
     let 
     updatedItem = { item | text = updatedText } 
     in 
     ({ model | rootItem = (updateItem model.rootItem updatedItem) }, Cmd.none) 

bu

ben

updateItem: Item.Item -> Item.Item -> Item.Item 
updateItem rootItem updatedItem = 
    if rootItem.id == updatedItem.id then 
    updatedItem 
    else 
    case rootItem.children of 
     Item.ChildItem [] -> 
     rootItem 

     Item.ChildItem children -> 
     let 
      updatedChildren = 
      case children of 
       [] -> 
       [] 

       children -> 
       List.map (\item -> 
        updateItem rootItem item) children 
     in 
      { rootItem | children = Item.ChildItem updatedChildren } 

ile geldi ama rootItem dönen yerine çünkü ben bir Maximum call stack size exceeded hatayı

cevap

15

Eğer yığın taşması alıyoruz nedenidir alıyorum Item.ChildItems [] kasasında [].

Kodunuzu biraz değiştireceğim çünkü çekebileceğimiz bazı yaygın modeller var. Öncelikle, o, bir şey her türlü uygun olabilir ki en alttaki ağaç imsi yapıyı alıp daha genel olduğunu yapalım sadece Item: Bize her zaman kök düğüm olan bir yapı kazandırıyor

type Node a 
    = Node a (List (Node a)) 

ve her biri aynı zamanda herhangi bir sayıda çocuğu olan herhangi bir sayıda çocuğa sahip olabilir.

Yapacağınız algoritmayı düşünürsek, tanıdık bir kalıbı tahmin edebiliriz. Birden fazla öğeye sahip bir yapınız var ve her öğeyi ziyaret eden ve isteğe bağlı olarak değiştiren bir algoritma istiyorsunuz. Bu çok List.map gibi geliyor.

map : (a -> b) -> Node a -> Node b 
map f (Node item children) = 
    Node (f item) (List.map (map f) children) 

: O bizim genelleştirilmiş fonksiyon map aramak için iyi bir fikirdir böyle bir ortak deyim (Not: Sadece functors tökezledi!)

Ben fikir atmış yana bizgöre şimdi

type alias Item = 
    { id: String 
    , text: String 
    } 

: Node türü içine yerleştirdi ve çocukların, böylece gibi takma Item değiştirmeniz gerekir, id belirli bir değerle eşleşiyorsa, güncelleştirebilen bir işlevi olması yararlı olacaktır.

Şimdi
updateByID : String -> (Item -> Item) -> Item -> Item 
updateByID id f item = 
    if item.id == id then 
    f item 
    else 
    item 

bir bir güncelleme gerçekleştirmek için: gelecekte, gerçekleştirmek istediğiniz daha güncelleme işlevleri olabilir bu yana, aslında gerçekleştirmek istediğiniz işlevinden ayrı arama ve kimlik eşleştirme kısmını tutmak en iyisidir öğe, ağacın herhangi bir yerinde id ile eşleşiyorsa, bunu yapabilirsiniz:

map (updateByID "someID" (\x -> { x | text = "Woohoo!" })) rootNode 
+0

Mükemmel yanıt! Benim günümü yaptın :) –

+0

Bu kalıbı kullanarak Öğelerin iç içe geçmiş listesini nasıl güncelleştiririm? Vurduğum sorun, kayıt güncelleme ifadesinin, içinde göründüğü herhangi bir işlevin dönüş ifadesi olacak şekilde tasarlanmasıdır. Yeni bir Öğe oluşturmak için tasarlanmış bir işlevdir, dönüş ifadesi, öğenin değil, yeni öğe olmalıdır. Ebeveynlerin çocuk listesi, güncellemem gerekenler. Özyinelemeli (aka "tree") yapılarla nasıl baş edileceğine dair bir yazgımız var mı? Öğeyi bir tür takma ad ve koleksiyon türünde yapma hakkında talimatlar gördüm; farklı bir protokol sunuyorsunuz. –

+0

@RichardHaven, harita işlevi tarafından ele alındığında, gövdesinde, Düğüm tarafından verilen ve muhtemelen işlev tarafından değiştirilen öğeden oluşan bir Düğüm ve Düğümden gelen çocukların her birinin de verildiğini fark eder. muhtemelen işlev tarafından değiştirildi. Sonuçta, updateById'yi haritaya beslerken, değiştirilen bir öğe dışında, aynı parçaları kullanarak düğüm ağacını yinelemeli olarak oluşturacaktır. –