diff --git a/3_Advanced_Data_Types.ipynb b/3_Advanced_Data_Types.ipynb index abaa7dde6bc1af46dd742b80d74b6fc62691fe64..bb61f351c2d59a9bcc28a3dfd14bedccae1efebd 100644 --- a/3_Advanced_Data_Types.ipynb +++ b/3_Advanced_Data_Types.ipynb @@ -348,7 +348,7 @@ "t3 = tuple()\n", "@show typeof(t3)\n", "\n", - "t4 = tuple(1, 'a', π)\n", + "t4 = tuple(1, 'a', π, 12.0)\n", "@show typeof(t4);" ] }, @@ -370,6 +370,24 @@ "t4[2]" ] }, + { + "cell_type": "markdown", + "id": "2519be57-8c10-4878-a772-017b099f6a4c", + "metadata": {}, + "source": [ + "It is also possible to get multiple elements by using slices." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c48ac07f-3a4e-4bb8-9551-2973ea8d2f9c", + "metadata": {}, + "outputs": [], + "source": [ + "@show t4[1:3];" + ] + }, { "cell_type": "markdown", "id": "aeda8602-f9da-4173-a692-54e2e9a0e178", @@ -591,10 +609,358 @@ "updated_person = (person..., number = \"0711 ...\")" ] }, + { + "cell_type": "markdown", + "id": "347fe4ea-5b63-4fb8-91bc-2b7b8a0c6a30", + "metadata": {}, + "source": [ + "### Exercises\n", + " - Write a function `swap_first_last` that takes a tuple and returns a new tuple with the first and last elements swapped. Tip: use a combination of the `...` operator and of this slice `[2:end-1]`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3721b654-b182-426d-b799-a69e0b53a550", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "647530cd-480a-414f-a8ec-1375e05a9e1b", + "metadata": {}, + "source": [ + "## Structs" + ] + }, + { + "cell_type": "markdown", + "id": "00b9bdb1-86ad-47f6-b8c3-88e85878eaff", + "metadata": {}, + "source": [ + "At this point you know how to use functions to organize code and built-in types to organize data. The next step is to learn how to build your own types to organize both code and data.\n", + "\n", + "We have used many of Julia’s built-in types; now we are going to define a new type. As an example, we will create a type called Point that represents a point in two-dimensional space.\n", + " \n", + "There are several ways we might represent points in Julia:\n", + " - We could store the coordinates separately in two variables, x and y.\n", + " - We could store the coordinates as elements in an tuple.\n", + " - We could create a new type to represent points as objects.\n", + "\n", + "Here we will investigate the third option." + ] + }, + { + "cell_type": "markdown", + "id": "9aa53d46-033b-4559-a5d8-e95357a257a5", + "metadata": {}, + "source": [ + "A programmer-defined composite type is also called a struct. The struct definition for a point looks like this:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c2e3f646-2d82-46a4-a265-01b919412c6d", + "metadata": {}, + "outputs": [], + "source": [ + "struct Point\n", + " x::Real\n", + " y::Real\n", + "end" + ] + }, + { + "cell_type": "markdown", + "id": "fb9be134-9730-4dbd-9e05-51dd94e9d8e0", + "metadata": {}, + "source": [ + "The header indicates that the new struct is called Point. The body defines the attributes or fields of the struct. The Point struct has two fields: x and y.\n", + "\n", + "A struct is like a factory for creating objects. To create a point, you call Point as if it were a function having as arguments the values of the fields. When Point is used as a function, it is called a constructor. The constructor returns an instance of the object." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "197339a2-0d8b-4882-bab2-80ba56410f21", + "metadata": {}, + "outputs": [], + "source": [ + "p = Point(3.0, 4.0)\n", + "@show p;" + ] + }, + { + "cell_type": "markdown", + "id": "b0134aa7-f356-4d71-9d46-959bd3cfe89a", + "metadata": {}, + "source": [ + "The type speficication (`::Real`) is optional, but can be helpful to enforce correct usage of the struct." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "849f1460-d228-4b22-a797-1077d75d1a2d", + "metadata": {}, + "outputs": [], + "source": [ + "p = Point(\"a\", 4.0)" + ] + }, + { + "cell_type": "markdown", + "id": "e3011b23-816d-4aa2-be61-7eb8f4aee9d1", + "metadata": {}, + "source": [ + "The values of the fields can be accessed using the `.` operator." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "00d090e4-d74d-4fad-ae8b-369c1046e48b", + "metadata": {}, + "outputs": [], + "source": [ + "@show p.x p.y;\n", + "@show distance = sqrt(p.x^2 + p.y^2);" + ] + }, + { + "cell_type": "markdown", + "id": "08748a8c-f76c-4f14-b3d2-9db99fb28e87", + "metadata": {}, + "source": [ + "Structs are however by default immutable, after construction the fields can not change value:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91bd6248-a358-43e3-90cc-6b8398445aaf", + "metadata": {}, + "outputs": [], + "source": [ + "p.x = 2" + ] + }, + { + "cell_type": "markdown", + "id": "629c1004-5d0f-4093-b546-473c94c91b44", + "metadata": {}, + "source": [ + "Where required, mutable composite types can be declared with the keyword mutable struct. Here is the definition of a mutable point:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "804c2c32-7a84-4261-88d8-1bad6ba5e18c", + "metadata": {}, + "outputs": [], + "source": [ + "mutable struct MPoint\n", + " x::Real\n", + " y::Real\n", + "end\n", + "p = MPoint(3.0, 4.0)\n", + "p.x = 2.0\n", + "@show p;" + ] + }, + { + "cell_type": "markdown", + "id": "1bd558dd-e6c3-4dc0-9476-768c59178708", + "metadata": {}, + "source": [ + "A third option is to let some fields of an unmutable struct to be mutable. For example a dictionary inside an unmutable struct can be modified." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "62cab9ae-5e58-401c-bc93-17720914e530", + "metadata": {}, + "outputs": [], + "source": [ + "struct Book\n", + " title::String\n", + " author::String\n", + " properties::Dict{String, Any}\n", + "end\n", + "\n", + "book = Book(\"Der Hobbit\", \"J.R.R. Tolkien\", Dict(\"available\" => false))\n", + "@show book\n", + "book.properties[\"available\"] = true\n", + "@show book;" + ] + }, + { + "cell_type": "markdown", + "id": "31e2098b-5079-4cb5-bf64-a7a78754af32", + "metadata": {}, + "source": [ + "You can pass an instance as an argument in the usual way. For example:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e6113221-0c03-484a-99e0-f7d4fab70f03", + "metadata": {}, + "outputs": [], + "source": [ + "function printpoint(p)\n", + " println(\"($(p.x), $(p.y))\")\n", + "end\n", + "printpoint(p)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d5c6fa9e-c31d-4aff-a3fd-db3449904519", + "metadata": {}, + "outputs": [], + "source": [ + "function printbook(book)\n", + " println(\"Title: $(book.title)\")\n", + " println(\"Author: $(book.author)\")\n", + " available = book.properties[\"available\"]\n", + " println(\"Available: $(available)\")\n", + "end\n", + "printbook(book)" + ] + }, + { + "cell_type": "markdown", + "id": "3b12a004-81d9-4bbc-9e2e-d2c281dcbe18", + "metadata": {}, + "source": [ + "Functions can return instances as return values." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c57c9f7-c4fd-4263-a55f-62f123aaaa8a", + "metadata": {}, + "outputs": [], + "source": [ + "function find_center(point1, point2)\n", + " x = (point1.x + point2.x)/2.0\n", + " y = (point1.y + point2.y)/2.0\n", + " return Point(x, y)\n", + "end\n", + "\n", + "point1 = Point(0.0, 0.0)\n", + "point2 = Point(10.0, 10.0)\n", + "\n", + "@show find_center(point1, point2);" + ] + }, + { + "cell_type": "markdown", + "id": "078b31ab-11e1-4596-8271-f0d33418eab8", + "metadata": {}, + "source": [ + "### Exercise\n", + "\n", + " - Write a function called `point_distance` which takes two points as arguments and returns the Euclidean distance between them." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5957a05-e396-41c3-9b8f-f14f089115ab", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "0fb1f6bc-e6e5-4897-bbb6-880e40e3bf20", + "metadata": {}, + "source": [ + "### References and values\n", + "\n", + "Each object (instance of a struct) is stored at some memory address. The operator `===` checks if two variables point to the same memory address of the object. For example\n", + "\n", + "book_copy = book\n", + "@show book_copy === book;" + ] + }, + { + "cell_type": "markdown", + "id": "636b6713-f4ed-4220-a159-2918bd44f78e", + "metadata": {}, + "source": [ + "This means that any change made to `book_copy` will also modify `book`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a9c46bac-d234-4616-a604-63bdf03b1393", + "metadata": {}, + "outputs": [], + "source": [ + "book.properties[\"available\"] = true\n", + "book_copy.properties[\"available\"] = false\n", + "\n", + "@show book;" + ] + }, + { + "cell_type": "markdown", + "id": "6bcbf983-40fa-4fc8-8eca-7cd7c45778dc", + "metadata": {}, + "source": [ + "If a new, distinct, object is needed (in other words, a copy by value), we can use the function `deepcopy`. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c36d23c8-2292-4568-aef4-7416c6f3e538", + "metadata": {}, + "outputs": [], + "source": [ + "book_copy = deepcopy(book)\n", + "@show book_copy === book;" + ] + }, + { + "cell_type": "markdown", + "id": "02cc3ec7-e712-4c4b-abf5-9bfc98e68a91", + "metadata": {}, + "source": [ + "Finally, the `==` operator between structs defaults to the `===` operator as Julia has no way of knowing how to compare custom structs. However, it is always possible to reimplement the `==` operator for our custom types." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3372eb89-cde6-4040-aa51-b4e76af97851", + "metadata": {}, + "outputs": [], + "source": [ + "import Core\n", + "\n", + "function ==(book1::Book, book2::Book)\n", + " return book1.title == book2.title && book1.author == book2.author\n", + "end" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "3409d1a3-85da-470c-bc5b-4ba9ecb53591", + "id": "03594f56-48ee-4152-a42f-8e3588c3ab2e", "metadata": {}, "outputs": [], "source": []