给出以下fileSystem结构:
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 } )
]
}
)
]
}
)
]
我希望能够 动态创建新文件夹 ,方法是调用一个函数并传入位置,我想要新文件夹到创建.. 文件夹的名称以及 fileSystem Dict 。像这样:
createFolder: String -> FileSystem -> FolderName -> FileSystem
createFolder "parent1/child3/sub-child_of_child3" fileSystem "DynamicallyCreated"
因为没有办法预先知道fileSystem Dict
的样子,并且因为这是elm(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.
使用递归查找相应的dict:
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
你可以在这里看到两大问题:
parent1
等更高级别更新我现在拥有的内容?例如: - 请记住,此更新可能会发生在20级以上的深度。如何告知级别3 2,1关于此更新?我不一定试图使这段代码有效。我有另一种方法,它甚至更好。
无法找到动态更新或创建Dicts中的Dicts的示例。
我现在正在与这个问题作斗争2-3天。
首先我尝试使用记录而不是Dicts - 因为它们允许在其中使用不同的类型。但我不能使用record."someString"
来访问它的价值 - 就像在javascript中一样。所以记录没有运气。 Dicts似乎更有希望..希望有人知道如何解决这个问题。谢谢:))
答案 0 :(得分:2)
这是一个很好的挑战!
首先,您正在处理递归类型(more on those here)。 Folder
包含Dict FolderName Folder
,所以你确实需要一些强类型。
您希望对Dict
内的Dict
进行递归更新。
您可以在下面找到示例解决方案代码,您可以将其复制/粘贴到http://elm-lang.org/try
不同功能的内部工作原理在代码本身中进行了解释。
一些意见:
Dict
,常见的模式是为整个Dict
(Dict.update
上的文档see here)提供密钥+更新功能。这比以下3步方法更有效:1)检索要更新的记录,2)更改记录和3)将其放回Dict
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
答案 1 :(得分:0)
上面给出的答案有效 - 但是如果你有一个围绕这个想法的问题(不一定用dicts) - 那么值得研究how zippers work
。
拉链是在dicts,list,trees上进行这种行走的推荐方法。更多信息:Learn You A Haskel - Zippers - 最后是文件系统实现。