forked from steger/pr3-ws202627
Merge remote-tracking branch 'upstream/main'
# Conflicts: # go/01-basics/calculator.go # go/01-basics/inventory.go # go/02-next-level/02-struct-embedding.go
commit
e17734dab4
|
|
@ -1,3 +1,3 @@
|
|||
module gitty.informatik.th-mannheim.de/steger/pr3-sose2026/go/00-hello-world/hello-world
|
||||
module gitty.informatik.th-mannheim.de/steger/pr3-ws202526/go/00-hello-world/hello-world
|
||||
|
||||
go 1.26.0
|
||||
|
|
|
|||
|
|
@ -13,33 +13,23 @@ func main() {
|
|||
exit = "exit"
|
||||
)
|
||||
|
||||
//TODO: implement according to README.md
|
||||
fmt.Println("Welcome to the Simple Calculator!")
|
||||
for {
|
||||
var operation string
|
||||
fmt.Print("\nEnter operation (add, subtract, multiply, divide, exit): ")
|
||||
fmt.Scan(&operation)
|
||||
|
||||
//the following code just demonstrates how to use fmt.Scan
|
||||
if operation == exit {
|
||||
fmt.Println("Goodbye!")
|
||||
break
|
||||
}
|
||||
|
||||
var str string
|
||||
fmt.Print("Please enter a string: ")
|
||||
fmt.Scan(&str)
|
||||
var num1, num2 float64
|
||||
fmt.Print("Enter the first number: ")
|
||||
fmt.Scan(&num1)
|
||||
fmt.Print("Enter the second number: ")
|
||||
fmt.Scan(&num2)
|
||||
|
||||
var x float64
|
||||
fmt.Print("Please enter a float: ")
|
||||
fmt.Scan(&x)
|
||||
|
||||
fmt.Printf("You entered '%s' and %f\n", str, x)
|
||||
switch operation {
|
||||
case add:
|
||||
fmt.Printf("Result: %.2f\n", num1+num2)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
package main
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Category int
|
||||
|
||||
const (
|
||||
Electronics Category = iota
|
||||
Groceries
|
||||
Clothes
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// a new type named Category that can hold integer values
|
||||
type Category int
|
||||
|
||||
// The go equivalent of an enum that enumerates different named Categories
|
||||
const (
|
||||
Electronics Category = iota //0
|
||||
Groceries //1
|
||||
Clothes //2
|
||||
)
|
||||
|
||||
// implementation of the fmt.Stringer interface. In that way, Categories can be printed using the fmt.Print* functions
|
||||
func (c Category) String() string {
|
||||
switch c {
|
||||
case Electronics:
|
||||
|
|
@ -23,99 +28,93 @@ func (c Category) String() string {
|
|||
}
|
||||
}
|
||||
|
||||
// Struct for product details
|
||||
type Product struct {
|
||||
Name string
|
||||
Price float64
|
||||
Quantity int
|
||||
Category string //TODO: use enum instead
|
||||
Category Category
|
||||
}
|
||||
|
||||
func addProduct(inventory *[]Product, name string, price float64, quantity int, category string) {
|
||||
//TODO: implement
|
||||
// Add a new product to the inventory
|
||||
func addProduct(inventory *[]Product, name string, price float64, quantity int, category Category) {
|
||||
// Check if the product already exists in the inventory
|
||||
for _, product := range *inventory {
|
||||
if product.Name == name {
|
||||
fmt.Println("Product already exists.")
|
||||
return
|
||||
}
|
||||
}
|
||||
// Add the new product
|
||||
*inventory = append(*inventory, Product{
|
||||
Name: name,
|
||||
Price: price,
|
||||
Quantity: quantity,
|
||||
Category: category,
|
||||
})
|
||||
fmt.Printf("Added product: %s\n", name)
|
||||
// implementation of the fmt.Stringer interface for printing products
|
||||
func (p Product) String() string {
|
||||
return fmt.Sprintf("%s (%s, Price: $%.2f, Quantity: %d)", p.Name, p.Category, p.Price, p.Quantity)
|
||||
}
|
||||
|
||||
// Remove a product from the inventory
|
||||
func removeProduct(inventory *[]Product, name string) {
|
||||
//TODO: implement
|
||||
for i, product := range *inventory {
|
||||
if product.Name == name {
|
||||
*inventory = append((*inventory)[:i], (*inventory)[i+1:]...)
|
||||
fmt.Printf("Removed product: %s\n", name)
|
||||
return
|
||||
}
|
||||
func addProduct(inventory map[string]Product, name string, price float64, quantity int, category Category) {
|
||||
|
||||
//do not create a new product in case a product with the name already exists
|
||||
if _, exists := inventory[name]; exists {
|
||||
fmt.Printf("Unable to add new Product %s: Product already exists.\n", name)
|
||||
return
|
||||
}
|
||||
fmt.Println("Product not found.")
|
||||
|
||||
//create a new product...
|
||||
p := Product{name, price, quantity, category}
|
||||
inventory[name] = p // ...and insert it into the map
|
||||
}
|
||||
|
||||
// Update the quantity of a product
|
||||
func updateQuantity(inventory *[]Product, name string, newQuantity int) {
|
||||
//TODO: implement
|
||||
for i, product := range *inventory {
|
||||
if product.Name == name {
|
||||
(*inventory)[i].Quantity = newQuantity
|
||||
fmt.Printf("Updated quantity for %s: New Quantity = %d\n", name, newQuantity)
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Println("Product not found.")
|
||||
func removeProduct(inventory map[string]Product, name string) {
|
||||
//no checks required. Does nothing if the product doesn't exist.
|
||||
delete(inventory, name)
|
||||
}
|
||||
|
||||
// Display the inventory
|
||||
func displayInventory(inventory []Product) {
|
||||
//TODO: implement
|
||||
fmt.Println("Inventory:")
|
||||
for _, product := range inventory {
|
||||
fmt.Printf("%s - %s (Price: $%.2f, Quantity: %d)\n",
|
||||
product.Name, product.Category, product.Price, product.Quantity)
|
||||
func updateQuantity(inventory map[string]Product, name string, newQuantity int) {
|
||||
p := inventory[name] //read from the map
|
||||
p.Quantity = newQuantity //update value
|
||||
inventory[name] = p //store back into the map
|
||||
}
|
||||
|
||||
func displayInventory(inventory map[string]Product) {
|
||||
//Iterate over the inventory
|
||||
i := 1
|
||||
for _, p := range inventory { //don't care about the key (_), only the value (p) is relevant
|
||||
fmt.Printf("%d. %s\n", i, p)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Initialize inventory
|
||||
inventory := []Product{
|
||||
{Name: "Laptop", Price: 1000, Quantity: 5, Category: "Electronics"},
|
||||
{Name: "Apples", Price: 2, Quantity: 50, Category: "Groceries"},
|
||||
{Name: "T-shirt", Price: 10, Quantity: 20, Category: "Clothes"},
|
||||
{Name: "Laptop", Price: 1000, Quantity: 5, Category: Electronics},
|
||||
{Name: "Apples", Price: 2, Quantity: 50, Category: Groceries},
|
||||
{Name: "T-shirt", Price: 10, Quantity: 20, Category: Clothes},
|
||||
|
||||
fmt.Println("Welcome to the Inventory Manager!")
|
||||
|
||||
// Store a fixed list of initial product names in an array.
|
||||
initialProductNames := [...]string{"Laptop"}
|
||||
|
||||
// Convert the array into a slice so the inventory can grow or shrink dynamically.
|
||||
productNames := initialProductNames[:]
|
||||
productNames = append(productNames, "Apples", "T-shirt")
|
||||
|
||||
// Use a map to associate product names with details like quantity, price, and category.
|
||||
prices := []float64{1000.0, 2.0, 10.0}
|
||||
quantities := []int{5, 50, 20}
|
||||
categories := []Category{Electronics, Groceries, Clothes}
|
||||
|
||||
inventory := make(map[string]Product)
|
||||
for i, name := range productNames {
|
||||
inventory[name] = Product{name, prices[i], quantities[i], categories[i]}
|
||||
}
|
||||
|
||||
// Display initial inventory
|
||||
fmt.Println("\nInitial Inventory:")
|
||||
displayInventory(inventory)
|
||||
|
||||
// Add a new product
|
||||
addProduct(&inventory, "Phone", 800, 10, "Electronics")
|
||||
addProduct(&inventory, "Phone", 800, 10, Electronics)
|
||||
fmt.Println("\nAdding a new product: Phone (Electronics, Price: $800, Quantity: 10)")
|
||||
addProduct(inventory, "Phone", 800, 10, Electronics)
|
||||
|
||||
// Display updated inventory
|
||||
fmt.Println("\nUpdated Inventory:")
|
||||
displayInventory(inventory)
|
||||
|
||||
// Update the quantity of an existing product
|
||||
updateQuantity(&inventory, "Apples", 30)
|
||||
fmt.Println("\nUpdating quantity for Apples: New Quantity = 30")
|
||||
updateQuantity(inventory, "Apples", 30)
|
||||
|
||||
// Remove a product
|
||||
removeProduct(&inventory, "T-shirt")
|
||||
fmt.Println("\nRemoving product: T-shirt")
|
||||
removeProduct(inventory, "T-shirt")
|
||||
|
||||
// Display final inventory
|
||||
fmt.Println("\nFinal Inventory:")
|
||||
displayInventory(inventory)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Counter struct {
|
||||
value int
|
||||
}
|
||||
|
||||
// pointer receiver. The method operates directly on the object c points to
|
||||
// Use a pointer receiver whenever the object needs to be modified.
|
||||
func (c *Counter) Increment() {
|
||||
c.value++
|
||||
}
|
||||
|
||||
// value receiver. The method operates on copy of object c. Use a value
|
||||
// receiver when the object does not need to be modified in the method.
|
||||
func (c Counter) String() string {
|
||||
return fmt.Sprintf("Counter value: %d", c.value)
|
||||
}
|
||||
|
||||
func main() {
|
||||
c := Counter{}
|
||||
fmt.Println(c)
|
||||
c.Increment()
|
||||
fmt.Println(c)
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type geometry interface {
|
||||
area() float64
|
||||
perim() float64
|
||||
}
|
||||
|
||||
type rect struct {
|
||||
width, height float64
|
||||
}
|
||||
|
||||
// Ensure that rect implements the geometry interface
|
||||
var _ geometry = rect{}
|
||||
|
||||
type circle struct {
|
||||
radius float64
|
||||
}
|
||||
|
||||
// Ensure that circle implements the geometry interface
|
||||
var _ geometry = circle{}
|
||||
|
||||
func (r rect) area() float64 {
|
||||
return r.width * r.height
|
||||
}
|
||||
func (r rect) perim() float64 {
|
||||
return 2*r.width + 2*r.height
|
||||
}
|
||||
|
||||
func (c circle) area() float64 {
|
||||
return math.Pi * c.radius * c.radius
|
||||
}
|
||||
func (c circle) perim() float64 {
|
||||
return 2 * math.Pi * c.radius
|
||||
}
|
||||
|
||||
//Metods demonstrates how to use an interface
|
||||
func measure(g geometry) {
|
||||
fmt.Println(g)
|
||||
fmt.Println(g.area())
|
||||
fmt.Println(g.perim())
|
||||
}
|
||||
|
||||
|
||||
//Method demonstrates casting an interface to a concrete type.
|
||||
//Should be avoided in practice whenever possible.
|
||||
func detectCircle(g geometry) {
|
||||
if c, ok := g.(circle); ok {
|
||||
fmt.Println("circle with radius", c.radius)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := rect{width: 3, height: 4}
|
||||
c := circle{radius: 5}
|
||||
|
||||
measure(r)
|
||||
measure(c)
|
||||
|
||||
detectCircle(r)
|
||||
detectCircle(c)
|
||||
}
|
||||
|
|
@ -11,12 +11,12 @@ func (p Person) Say() {
|
|||
}
|
||||
|
||||
type Student struct {
|
||||
Person //struct Person is embedded
|
||||
Person //struct Person is embedded
|
||||
Semester int
|
||||
}
|
||||
|
||||
type Teacher struct {
|
||||
Person //struct Person is embedded
|
||||
Person //struct Person is embedded
|
||||
Subject string
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ package main
|
|||
|
||||
import "fmt"
|
||||
|
||||
func SlicesIndex[S []E, E int](s []string, v string) int {
|
||||
// a generic function that takes a slice of any type and a value of that type, and returns the index of the value in the slice, or -1 if it is not found.
|
||||
// The type parameters S and E are declared in square brackets before the function name, and they can be used in the function signature and body.
|
||||
// The constraint comparable means that the type E must support the == operator, which is necessary for comparing the value with the elements of the slice.
|
||||
func SlicesIndex[S []E, E comparable](s S, v E) int {
|
||||
for i := range s {
|
||||
if v == s[i] {
|
||||
return i
|
||||
|
|
@ -11,27 +14,34 @@ func SlicesIndex[S []E, E int](s []string, v string) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
type List struct {
|
||||
head, tail *element
|
||||
// represents a linked list of any type T. The type parameter T is used in the definition of List and its methods,
|
||||
// and it can be instantiated with any type when we create a List value. The type any is in fact defined as
|
||||
// an empty interface. Thanks to duck-typing it can be used to represent any type, but it does not provide any
|
||||
// operations on the values of that type.
|
||||
type List[T any] struct {
|
||||
head, tail *element[T]
|
||||
}
|
||||
|
||||
type element struct {
|
||||
next *element
|
||||
val int
|
||||
// the generic element of the linked list
|
||||
type element[T any] struct {
|
||||
next *element[T]
|
||||
val T
|
||||
}
|
||||
|
||||
func (lst *List) Push(v int) {
|
||||
// adds an instance of the generic type to the list
|
||||
func (lst *List[T]) Push(v T) {
|
||||
if lst.tail == nil {
|
||||
lst.head = &element{val: v}
|
||||
lst.head = &element[T]{val: v}
|
||||
lst.tail = lst.head
|
||||
} else {
|
||||
lst.tail.next = &element{val: v}
|
||||
lst.tail.next = &element[T]{val: v}
|
||||
lst.tail = lst.tail.next
|
||||
}
|
||||
}
|
||||
|
||||
func (lst *List) AllElements() []int {
|
||||
var elems []int
|
||||
// returns all elements of the list as a slice of the generic type.
|
||||
func (lst List[T]) AllElements() []T {
|
||||
var elems []T
|
||||
for e := lst.head; e != nil; e = e.next {
|
||||
elems = append(elems, e.val)
|
||||
}
|
||||
|
|
@ -39,13 +49,21 @@ func (lst *List) AllElements() []int {
|
|||
}
|
||||
|
||||
func main() {
|
||||
|
||||
var s = []string{"foo", "bar", "zoo"}
|
||||
// The types S and E are explicitly specified as []string and string respectively.
|
||||
fmt.Println("index of zoo:", SlicesIndex[[]string, string](s, "zoo"))
|
||||
|
||||
fmt.Println("index of zoo:", SlicesIndex(s, "zoo"))
|
||||
var s2 = []int{2, 4, 5, 6}
|
||||
// The type E is inferred from the type of the value 4, which is int,
|
||||
// and the type S is inferred from the type of the slice s2, which is []int.
|
||||
fmt.Println("index of 4: ", SlicesIndex(s2, 4))
|
||||
|
||||
lst := List{}
|
||||
// instantiate a list of float64 elements
|
||||
lst := List[float64]{}
|
||||
lst.Push(10)
|
||||
lst.Push(13)
|
||||
lst.Push(23)
|
||||
lst.Push(23.3)
|
||||
fmt.Println("list:", lst.AllElements())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,18 +6,22 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
f := createFile("/tmp/defer.txt")
|
||||
writeFile(f)
|
||||
closeFile(f)
|
||||
}
|
||||
|
||||
func createFile(p string) *os.File {
|
||||
fmt.Println("creating")
|
||||
f, err := os.Create(p)
|
||||
f, err := os.Create("/tmp/defer.txt")
|
||||
|
||||
// defer the closing of the file until the surrounding function returns.
|
||||
// This ensures that the file will be closed even if there is a panic or an early return in the function.
|
||||
defer closeFile(f)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return f
|
||||
|
||||
//will not be called in case of a panic before
|
||||
writeFile(f)
|
||||
|
||||
// closeFile will be called here due to the defer-statement, even in case of a panic before.
|
||||
}
|
||||
|
||||
func writeFile(f *os.File) {
|
||||
|
|
@ -27,8 +31,10 @@ func writeFile(f *os.File) {
|
|||
|
||||
func closeFile(f *os.File) {
|
||||
fmt.Println("closing")
|
||||
err := f.Close()
|
||||
|
||||
var err error
|
||||
if f != nil {
|
||||
err = f.Close()
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue