簡體   English   中英

F#基礎知識:地圖地圖?

[英]F# Basics: Map of Maps?

我的教授給出了我們關於解決問題的課程提示,並建議我們首先使用以下方法對課程進行建模:

let students = Map.add 1 Map.empty Map.empty

世界從一個學生開始(由他們的ID表示,例如1),每個學生都有許多班級ID,每個班級都有一個與他們相關的成績(A +,F等)。

我無法理解這段代碼的實際含義,我理解一張地圖。 例如

let student = Map.add 43 "A+" 

如何在建議的版本中添加新的“學生”或“classID / grade”並返回新地圖?

作為后續內容,我將如何訪問/迭代這樣的地圖上的元素? 謝謝,功能編程初學者在這里真的很掙扎。

好的,所以我想出了如何添加它,我只需要給每個Map一個表名,例如

let students = Map.add 1 (Map.add 43 "A+" Map.empty) Map.empty

我可以使用Let student1 = Map.find 1 students來獲取特定學生的地圖。

let grades = Map.empty<string,string>
let students = Map.empty<int,Map<string,string>>
let students = students.Add(1,grades.Add("class1","A").Add("class2","B"))
let students = students.Add(2,grades.Add("class3","A").Add("class4","C"))

您可以通過索引到地圖來訪問該值: students.[1] 將返回val it : Map<string,string> = map [("class1", "A"); ("class2", "B")] val it : Map<string,string> = map [("class1", "A"); ("class2", "B")]

另外兩條評論:1)通常,您首先會構建某種數據結構,例如元組列表,然后從那里構建映射。

let classes = [("class1","A");("class2","B")]
classes |> Map.ofList

2)您可以通過Map.iter或Map.map迭代地圖:

let classes' = classes |> Map.ofList

並防止等級膨脹:-)

classes' |> Map.map (fun key value -> (key,value+"-"))

請查看: https//en.wikibooks.org/wiki/F_Sharp_Programming/Sets_and_Mapshttps://msdn.microsoft.com/en-us/library/ee353880.aspx

雖然我能理解答案,但是在嘗試s952163(重復變量聲明)的答案時出現了語法錯誤,所以我想發布一個可能有幫助的替代方案。

希望代碼中的注釋解釋建議。

// Alias the types for ease of use later
let studentRecords = Map.empty<int, Map<string, string>>
let grades = Map.empty<string, string>

// create a collection of students 
// (note use of aliases here but could use Map.empty<>.Add if preferred)
let students = 
    studentRecords
        .Add(1, grades.Add("class1", "A").Add("class2", "B"))
        .Add(2, grades.Add("class1", "D").Add("class2", "C"))

// can index into the student map by the integer key
// and print out the value of the embedded map based on it's key
let studentId = 1
let className = "class2"
let student1 = students.[studentId]

printfn 
    "StudentId: %i obtained %s in %s" 
    studentId 
    student1.[className] 
    className

// can use Map.map to iterate and obtain some specifics from the embedded map
// eg.  This prints out all students and their grades for "class2"
printfn "%s results:" className
students 
    |> Map.map (fun key value -> printfn "Student %i - %s" key value.[className] )

我剛開始使用f#,所以s952163的答案確實幫助我達到了上述目標。 希望它為進入這個問題的人增加了進一步的見解。

關於你的問題, let student = Map.add 43 "A+"意味着: Map.add需要3個參數,一個鍵,一個值和一個要添加的地圖。 因此, student是一個獲取地圖的函數,並返回一個新地圖,該地圖還包含值為"A+"的鍵43

關於數據類型的另一個建議:成績不是自由格式字符串。 您可以使用與域緊密匹配的數據類型使代碼更安全。 例如,對於成績,您可以使用

type Grade = 
    | APlus
    | A
    | B
    | F // Omitting the other grades for brevity

如果你這樣做並且很好地使用模式匹配,編譯器將幫助你檢查你可能忽略的角落的代碼。 同樣,您可以避免將識別學生的整數與識別類的整數混合在一起。

[<Measure>]
type StudentID
[<Measure>]
type ClassID
// A class that contains 2 students with IDs 1 and 2
let initialClass = Set.ofList [ 1<StudentID>; 2<_> ]
// Adding a student with ID 3 gives a new class
let classWithNewStudent = initialClass.Add 3<_>

請注意,您只需要在一個位置添加類型注釋,並且可以在其余位置使用<_> 這當然不是萬無一失的 - 您仍然可以做1<StudentID> + 2<StudentID> ,但至少您無法使用學生ID索引每個班級的地圖。

有了這些,您現在可以為類中的成績構建地圖:

let gradesForClass101 = 
    [ (1<StudentID>, APlus); (2<_>, F) ]
    |> Map.ofList

let gradesForClass206 = 
    [ (3<StudentID>, B); (2<_>, F) ]
    |> Map.ofList
// Here's the student function from your question:
let addStudent43 = Map.add 43<_> APlus
// This is now a new map that contains students 2, 3, 43
let moreGrades = addStudent43 gradesForClass206

使用類內地圖,您現在可以定義從classID到studentID到grade的地圖:

// This is now a map of maps: For each class, we store 
// a mapping classID -> grades that all students got in this class
let gradesPerClass = 
    [ (206<ClassID>, gradesForClass206); (101<_>, gradesForClass101)]
    |> Map.ofList

您還在一條評論中詢問了mutable / immutable - 不完全確定問題是什么,但這里有兩種方法可以為學生ID 2積累成績,具有不可變和可變的數據結構:

// Compute all grades that student 2 gets
let gradesViaFold = 
    gradesPerClass
    |> Map.fold (fun state _ grades ->
        // We don't need the classID here, so use _ as the second arg
        match grades.TryFind 2<_> with
        // We found a grade for student 2: Add it at the beginning of the list
        | Some grade -> grade :: state
        // Student 2 did not get a grade for this class: leave the state
        // (grades seen so far) empty
        | _ -> state
    ) []
let printGrades g = Seq.iter (printfn "Grade %A") g
do printGrades gradesViaFold

// Compute all grades that student 2 gets, via a mutable list
let gradesMutable = System.Collections.Generic.List<Grade>()
do 
    gradesPerClass
    |> Map.iter (fun _ grades ->
        match grades.TryFind 2<_> with
        | Some grade -> gradesMutable.Add grade
        | _ -> ()
    )
    printGrades gradesMutable

(使用do來突出顯示具有副作用的部分,在大多數情況下do

這是另一個如何瀏覽地圖的例子:

// A map from classID to grade for student 2
let gradesForStudent2 = 
    gradesPerClass
    |> Seq.choose (fun (KeyValue(classID, grades)) -> 
        match grades.TryFind 2<_> with
        | Some grade -> Some (classID, grade)
        | _ -> None
    )
    |> Map.ofSeq

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM