Podsumowanie — bezpieczne dzielenie
Część sekcji Logika i przepływ sterowania ścieżki GO w Coddy. Lekcja 44 z 68.
Wyzwanie
ŁatwyStwórz wszechstronny kalkulator matematyczny, który implementuje bezpieczne dzielenie z wieloma niestandardowymi typami błędów i prawidłową obsługą błędów. To wyzwanie łączy niestandardowe typy błędów, opakowywanie błędów i sprawdzanie błędów, aby utworzyć niezawodny system, który sprawnie obsługuje różne niepowodzenia operacji matematycznych.
Otrzymasz dwa wejścia:
- Łańcuch zawierający szczegóły operacji w formacie
"operation,num1,num2,precision"(np."divide,10,0,2") - Łańcuch zawierający ustawienia kalkulatora w formacie
"calculator_name,mode"(np."ScientificCalc,strict")
Twoje zadanie:
- Utworzyć dwa niestandardowe typy błędów:
- strukturę
DivisionErrorz polamiDividend(float64) iDivisor(float64) - strukturę
ValidationErrorz polamiField(string) iValue(float64)
- strukturę
- Zaimplementować metodę
Error() stringdlaDivisionError, która zwraca:"division error: cannot divide [dividend] by [divisor]" - Zaimplementować metodę
Error() stringdlaValidationError, która zwraca:"validation error: invalid [field] value [value]" - Przeanalizować pierwsze wejście, dzieląc je po przecinkach, aby uzyskać operację, num1, num2 i precision
- Przeanalizować drugie wejście, dzieląc je po przecinkach, aby uzyskać nazwę kalkulatora i tryb
- Przekonwertować liczby zapisane jako łańcuchy na float64, a precision na int
- Utworzyć funkcję
safeDivide, która przyjmuje dwa parametry float64 i zwraca (float64, error):- Jeśli dzielnik wynosi 0, zwrócić 0 i
DivisionErrorz wartościami dzielnej i dzielnika - Jeśli dzielna jest ujemna, a tryb to
"strict", zwrócić 0 iValidationErrorz polem"dividend"oraz wartością dzielnej - Jeśli dzielnik jest ujemny, a tryb to
"strict", zwrócić 0 iValidationErrorz polem"divisor"oraz wartością dzielnika - W przeciwnym razie zwrócić wynik dzielenia i nil
- Jeśli dzielnik wynosi 0, zwrócić 0 i
- Utworzyć funkcję
performCalculation, która opakowuje wyniksafeDivide:- Jeśli
safeDividezwraca błąd, opakować go w:"calculation failed in [calculator_name]: %w" - Jeśli operacja zakończy się powodzeniem, zwrócić wynik i nil
- Jeśli
- Wywołać
performCalculationi obsłużyć wynik:- Jeśli nie ma błędu: wypisać
"Calculation successful: [result_formatted_to_precision_decimal_places]" - Jeśli wystąpi błąd: wypisać
"Calculation failed: [error_message]"
- Jeśli nie ma błędu: wypisać
- Użyć
errors.As, aby sprawdzić konkretne typy błędów w opakowanym błędzie:- Wypisać
"Checking for division error: [true/false]" - Wypisać
"Checking for validation error: [true/false]"
- Wypisać
- Jeśli zostanie znaleziony
DivisionError, wypisać jego szczegóły:"Division Error Details:""Dividend: [dividend]""Divisor: [divisor]"
- Jeśli zostanie znaleziony
ValidationError, wypisać jego szczegóły:"Validation Error Details:""Field: [field]""Value: [value]"
- Wyświetlić końcowe podsumowanie:
"Calculator Summary:""Name: [calculator_name]""Mode: [mode]""Operation: [operation]""Input: [num1] [operation_symbol] [num2]", gdzie operation_symbol to "/" dla dzielenia"Precision: [precision] decimal places""Status: [Success/Failed]"
Użyj pakietu strings, aby podzielić wejściowe łańcuchy po przecinkach, pakietu strconv, aby przekonwertować łańcuchy na liczby, pakietu errors dla errors.As oraz pakietu fmt do opakowywania i formatowania błędów. To wyzwanie pokazuje, jak niestandardowe typy błędów, opakowywanie błędów i prawidłowe sprawdzanie błędów współdziałają, tworząc niezawodne systemy obsługi błędów, które dostarczają szczegółowych informacji o różnych typach niepowodzeń.
Spróbuj swoich sił
package main
import (
"errors"
"fmt"
"strconv"
"strings"
)
func main() {
// Wczytaj dane wejściowe
var operationInput string
var settingsInput string
fmt.Scanln(&operationInput)
fmt.Scanln(&settingsInput)
// Przeanalizuj dane wejściowe operacji (operation,num1,num2,precision)
operationParts := strings.Split(operationInput, ",")
operation := operationParts[0]
num1, _ := strconv.ParseFloat(operationParts[1], 64)
num2, _ := strconv.ParseFloat(operationParts[2], 64)
precision, _ := strconv.Atoi(operationParts[3])
// Przeanalizuj dane wejściowe ustawień (calculator_name,mode)
settingsParts := strings.Split(settingsInput, ",")
calculatorName := settingsParts[0]
mode := settingsParts[1]
// TODO: Napisz kod poniżej
// 1. Utwórz struktury DivisionError i ValidationError z metodami Error()
// 2. Zaimplementuj funkcję safeDivide
// 3. Zaimplementuj funkcję performCalculation
// 4. Wywołaj performCalculation i obsłuż wyniki
// 5. Użyj errors.As, aby sprawdzić, czy występują błędy określonych typów
// 6. Jeśli wystąpią błędy, wypisz ich szczegóły
// 7. Wyświetl końcowe podsumowanie
}Wszystkie lekcje w sekcji Logika i przepływ sterowania
1Zaawansowane sterowanie przepływem
Instrukcja switch z `fallthrough`Przerywanie zagnieżdżonych pętliKontynuowanie określonej pętliInstrukcja `goto`Podsumowanie — zaawansowane sterowanie pętlami4Projekt: Prosta lista zadań
Konfiguracja projektuDodawanie zadania7Obsługa błędów — dogłębnie
Własne typy błędówOpakowywanie błędów za pomocą `%w`Rozpakowywanie za pomocą `errors.Is`Rozpakowywanie za pomocą `errors.As`Zrozumienie `panic`Używanie `recover`Podsumowanie — bezpieczne dzielenie2Struktury i metody
Definiowanie metod dla strukturOdbiorniki przez wartośćOdbiorniki przez wskaźnikWybór odbiornikówMetody a funkcjePodsumowanie — działanie struktur5Mapy w szczegółach
Mapy strukturWskaźniki jako wartości mapSprawdzanie, czy mapy są nilPorównywanie mapPodsumowanie — licznik częstości słów3Interfejsy (podstawy)
Czym jest interfejs?Definiowanie interfejsuImplementowanie interfejsuUżywanie typów interfejsowychPusty interfejsAsercje typuPrzełącznik typówPodsumowanie – kształty i zachowaniaPoćwicz samodzielnie: Kompilator Go online