2016-11-15 48 views
0

考慮下面的文件系統結構:Elm:如果結構是動態的(具有不同的深度),如何更新Dict裏面的Dict?

type alias FolderName = String 

type Folder 
    = Folder 
     { folderName : String 
     , childFolders : Dict FolderName Folder 
     } 

type alias FileSystem = 
    Dict FolderName Folder 


fileSystem : FildeSystem 
fileSystem = 
    Dict.formList 
     [ ("parent1" 
      , Folder 
       { folderName = "parent1" 
       , childFolders = 
        Dict.fromList 
         [ ("child1", Folder { folderName = "child1", childFolders = Dict.empty }) 
         , ("child2", Folder { folderName = "child2", childFolders = Dict.empty }) 
         , ("child3" 
          , Folder 
           { folderName = "child3" 
           , childFolders = 
            Dict.formList 
             [ ("sub-child_of_child3", Folder { folderName = "sub-child_of_child3", childFolders = Dict.empty }) 
             ] 
           } 
         ) 
         ] 
       } 
     ) 
     ] 

我希望能夠動態創建新的文件夾,通過調用函數和傳遞在我希望創建新文件夾的位置文件夾名稱以及文件系統字典。事情是這樣的:

createFolder: String -> FileSystem -> FolderName -> FileSystem 
createFolder "parent1/child3/sub-child_of_child3" fileSystem "DynamicallyCreated" 

因爲沒有辦法前期知道fileSystem Dict是什麼樣子,因爲這是榆(no for loops) - 我認爲要做到這一點的唯一方法,就是使用遞歸。

代碼:

createFolder location fileSystem newFolderName= 
    let 
     locationAsArray = String.split "/" location 
    in 
        // first find the dict value. (that is the value of 'sub-child_of_child3' key, inside 'child3' Dict.) 

     findDictValueBasedOnLocation locationAsArray fileSystem 

        // then update the 'sub-child_of_child3' by inserting the newFolder. 
      |> (\ x -> { x | childFolders = Dict.insert newFolderName Folder { folderName = newFolderName, childFolders = Dict.empty } x.childFolders 

        // but how to reconsturct the child3, partent1 and finally the fileSystem now? Because this function it's supose to return a new fileSystem that contains the newly created folder. 

找到相應的字典遞歸:

findDictValueBasedOnLocation listDictKeys currentDict = 
    let 
     currentKey = 
      List.head listDictKeys 

     remainingKeys = 
      List.tail listDictKeys 
    in 
     -- when there is only one element in the listDictKeys that is: ["sub-child_of_child3"]- the recursive call stops/ 
     if List.length listDictKeys == 1 then 
      Dict.get currentKey currentDict 
       |> Maybe.withDefault -- what to add here? 
     else 
      let 
       nextDict = 
        Dict.get currentKey currentDict 
         |> Maybe.withDefault --what to add here ?- don't know the type .. 
      in 
       -- recursive call with the remaining listDictKeys and nextDict which is in fact the current Dict value. 
       findDictValueBasedOnContext remainingKeys nextDict 

這裏你可以看到2個大問題:

  1. 的Dict.get回報一個也許,我不知道如何處理智慧h在遞歸中。
  2. 即使我設法找到字典的相應部分,並通過創建新文件夾來更新它; - 我如何更新我現在在parent1等更高層次上的內容?例如: - 請記住,這個更新可能會發生在20級的水平上..我如何告訴3級2,1關於此更新?

我不一定會試着讓這段代碼有效。我還有另一種方法,它甚至更好。

而且找不到動態更新或在Dicts中創建Dicts的示例。

我現在正在爲這個問題作戰2-3天。

首先,我嘗試使用記錄而不是Dicts - 因爲它們允許其中的不同類型。但我沒有使用record."someString"來訪問它的值 - 就像在javascript中一樣。所以沒有記錄的運氣。字典似乎更有希望..希望有人知道如何解決這個問題。謝謝:)

回答

2

這是一個很好的挑戰! 首先,你正在處理遞歸類型(more on those here)。 A Folder包含一個Dict FolderName Folder,所以你確實需要在這裏打字。

而您正在尋找在DictDict上進行遞歸更新。

下面你可以找到的樣品溶液的代碼,它可以複製/粘貼到http://elm-lang.org/try

的不同功能的內部工作在代碼本身進行說明。

一些評論:

  • 對於更新Dict,一個共同的模式是提供一種鍵+(上Dict.updatesee here用於文檔)的更新功能,以整個Dict。這樣做更有效率,即1)檢索要更新的記錄,2)更改記錄和3)將其放回
  • 如果提供的路徑中的任何節點失敗,則該功能只會返回一個不變FileSystem
  • 如果newFolderName已經存在,與該名稱的整個現有文件夾(包括所有子文件夾)將被替換

希望這有助於理解一些在榆樹的功能。

代碼示例:

import Html exposing (text) 
import Dict exposing (Dict) 

---- TYPES 
type alias FolderName = String 

type Folder 
    = Folder 
     { folderName : FolderName 
     , childFolders : FileSystem 
     } 

type alias FileSystem = 
    Dict FolderName Folder 


{- MAIN FUNCTION: 
takes the first element in the path 
and tries to do a recursive update on the children of the fileSystem 
-} 
insertFolder: String -> FolderName -> FileSystem -> FileSystem 
insertFolder path newFolderName fileSystem = 
    let 
    nodeList = String.split "/" path 
    in 
    case nodeList of 
     node :: rest -> 
     -- if we have nodes, do recursive update on folders 
     fileSystem 
     |> Dict.update node (Maybe.map <| updateNestedFolder newFolderName rest) 

     [] -> 
     -- no path, so the new folder must be a root folder 
     fileSystem 
     |> Dict.inset newFolderName (newFolder newFolderName) 

{- Recursive update function where the magic happens 
-} 
updateNestedFolder : FolderName -> List FolderName -> Folder -> Folder 
updateNestedFolder newFolderName nodeList (Folder { folderName, childFolders }) = 
    case nodeList of 
    nextLevel :: rest -> 
     -- as long as there is a nodelist, we try to find deeper level 
     let 
     -- do recursive update on the children 
     newChildFolders = 
      childFolders 
      |> Dict.update nextLevel (Maybe.map <| updateNestedFolder newFolderName rest) 
     in 
     -- return the updated folder 
     Folder 
      { folderName = folderName 
      , childFolders = newChildFolders 
      } 

    [] -> 
     -- this is the lowest level, so we need to add to this FileSystem 
     let 
     -- add newFolderName to the child folders 
     newChildFolders = 
      childFolders 
      |> Dict.insert newFolderName (newFolder newFolderName) 
     in 
     -- return the folder 
     Folder 
      { folderName = folderName 
      , childFolders = newChildFolders 
      } 

---- HELPERS 

{- Create a new folder, without any children -} 
newFolder : String -> Folder 
newFolder folderName = 
    Folder 
    { folderName = folderName 
    , childFolders = Dict.empty 
    } 

------ EXAMPLE 


fileSystem = 
    Dict.fromList 
    [ ("parent1" 
     , Folder 
     { folderName = "parent1" 
     , childFolders = 
      Dict.fromList 
       [ ("child1" 
       , Folder 
        { folderName = "child1", childFolders = Dict.empty } 
       ) 
       , ("child2" 
       , Folder 
        { folderName = "child2", childFolders = Dict.empty } 
       ) 
       , ("child3" 
       , Folder 
        { folderName = "child3" 
        , childFolders = 
         Dict.fromList 
         [ ("sub-child_of_child3" 
          , Folder 
          { folderName = "sub-child_of_child3" 
          , childFolders = Dict.empty } 
         ) 
         ] 
        } 
       ) 
       ] 
      } 
     ) 
     ] 

main = 
    text <| toString <| 
    insertFolder 
     "parent1/child3/sub-child_of_child3" 
     "DynamicallyCreated" 
     fileSystem 
+0

你是如此聰明的人! :))非常感謝,你救了我。我會告訴你我用的是什麼 - 一旦它變得可以表達。有趣的是,使用語音命令在榆木中編程。也許你會幫我重構一些想法,一旦第一個演示工作..好吧,祝你好運! – AIon

+0

不客氣。總是樂於幫助! – wintvelt

0

上面給出的答案的工作 - 但如果你有問題圍繞這一理念旋轉(不一定與類型的字典) - 這是很值得考慮how zippers work

拉鍊是推薦的方式來做這種行走在字典,名單,樹木。更多這裏:Learn You A Haskel - Zippers - 最後是文件系統的實現。

相關問題