A tale of programming languages: Clojure
It was tested ChatGPT Code Copilot to generate the function "solve-quadratic" from the picture Raices.PNG :
FINAL CODE
===========
(ns quadratic-equation
(:require [clojure.math.numeric-tower :as math]
[clojure.test :refer :all]))
(defn complex-number
"Creates a complex number as a map with real and imaginary parts."
[real imag]
{:pre [(number? real) (number? imag)]}
{:real real :imag imag})
(defn solve-quadratic
"Solves the quadratic equation ax^2 + bx + c = 0.
Returns a map with the type of roots and the roots themselves.
Parameters:
- a: coefficient of x^2
- b: coefficient of x
- c: constant term
Returns:
- A map with :type key indicating the nature of roots (:linear, :real, or :complex)
and :roots key containing the roots.
- If inputs are invalid, returns an error message as a string."
[a b c]
{:pre [(number? a) (number? b) (number? c)]}
(let [compute-linear-root (fn [b c]
{:type :linear :root (/ (- c) b)})
compute-real-roots (fn [b discriminant denom]
(let [sqrt-d (math/sqrt discriminant)]
[(+ (- b) sqrt-d) (- (- b) sqrt-d)]))
compute-complex-roots (fn [b discriminant denom]
(let [sqrt-d (math/sqrt (- discriminant))
real-part (/ (- b) denom)
imag-part (/ sqrt-d denom)]
[(complex-number real-part imag-part)
(complex-number real-part (- imag-part))]))]
(cond
(and (= a 0) (= b 0) (= c 0)) "Infinite solutions"
(and (= a 0) (= b 0)) "No solution"
(= a 0) (compute-linear-root b c)
:else
(let [discriminant (- (* b b) (* 4 a c))
denom (* 2 a)]
(cond
(pos? discriminant)
{:type :real :roots (map #(/ % denom) (compute-real-roots b discriminant denom))}
(zero? discriminant)
{:type :real :roots [(/ (- b) denom)]}
:else
{:type :complex :roots (compute-complex-roots b discriminant denom)})))))
;; Unit Tests
(deftest test-solve-quadratic
(testing "Linear equation"
(is (= {:type :linear :root 2.0} (solve-quadratic 0 2 -4))))
(testing "Infinite solutions"
(is (= "Infinite solutions" (solve-quadratic 0 0 0))))
(testing "No solution"
(is (= "No solution" (solve-quadratic 0 0 2))))
(testing "Quadratic equation with two real roots"
(is (= {:type :real :roots [2.0 1.0]} (solve-quadratic 1 -3 2))))
(testing "Quadratic equation with one real root"
(is (= {:type :real :roots [1.0]} (solve-quadratic 1 -2 1))))
(testing "Quadratic equation with complex roots"
(let [result (solve-quadratic 1 1 1)
roots (:roots result)]
(is (= :complex (:type result)))
(is (= {:real -0.5 :imag 0.8660254037844386} (first roots)))
(is (= {:real -0.5 :imag -0.8660254037844386} (second roots)))))
(testing "Extreme coefficients - large values"
(let [result (solve-quadratic 1e10 -3e10 2e10)
roots (:roots result)]
(is (= :real (:type result)))
(is (every? number? roots))))
(testing "Extreme coefficients - small values"
(let [result (solve-quadratic 1e-10 -3e-10 2e-10)
roots (:roots result)]
(is (= :real (:type result)))
(is (every? number? roots))))
(testing "Invalid input handling"
(is (thrown? AssertionError (solve-quadratic "a" "b" "c")))
(is (thrown? AssertionError (solve-quadratic 1 "b" 2)))
(is (thrown? AssertionError (solve-quadratic 1 2 "c")))))
;; Run the tests
(run-tests)
===========
Initial prompt:
You are an expert programming Clojure. Can you write the best code in this language for the algorithm in the attached picture?
To implement the algorithm depicted in the image in Clojure, we need to solve quadratic equations of the form . The image shows the steps and different cases based on the discriminant .
Let's break down the problem:
- Linear case: If , the equation reduces to .
- Discriminant calculation: For , calculate the discriminant .
- Discriminant cases:
- If , there are two real roots.
- If , there is one real root.
- If , there are two complex roots.
Pseudocode
- Check if :
- If true, solve the linear equation .
- Calculate the discriminant .
- Evaluate the discriminant:
- If , compute the two real roots.
- If , compute the single real root.
- If , compute the two complex roots.
Explanation
- The
solve-quadratic
function handles different cases based on the value ofa
and the discriminant. - The
complex-number
function creates a map representing a complex number. - The example usages demonstrate solving quadratic equations with different coefficients.
Second prompt, following "Next steps" suggested:
Explanation
Type Hints and Documentation:
- Added
:pre
conditions to ensure the inputs are numbers. - Documented the
complex-number
andsolve-quadratic
functions with parameter and return descriptions.
- Added
Unit Tests:
- Created the
test-solve-quadratic
test suite using theclojure.test
framework. - Added tests for linear equations, infinite solutions, no solutions, quadratic equations with real roots, and quadratic equations with complex roots.
- For complex roots, the test verifies both the real and imaginary parts of the roots.
- Created the
Running Tests:
- The
run-tests
function runs all the tests in the namespace.
Third prompt, following "Next steps" suggested:
Explanation
Refactor for Edge Cases and Performance:
- Improved the condition checks for different cases.
- Simplified linear equation handling by checking specific cases directly.
Error Handling:
- Added assertions to ensure the inputs are numbers.
- Provided clear error messages for different invalid input scenarios.
Unit Tests:
- Expanded the test suite to cover additional edge cases and invalid inputs.
- Included tests for invalid inputs to ensure the function throws appropriate errors.
Fourth prompt, following "Next steps" suggested:
Explanation
Optimization:
- Extracted common computations into helper functions (
compute-linear-root
,compute-real-roots
, andcompute-complex-roots
). - Precomputed
denom
and reused it in the calculations. - Reduced repetition by defining functions for real and complex root computations.
- Extracted common computations into helper functions (
Error Handling:
- Maintained assertions to ensure inputs are valid numbers.
- Error messages are included for invalid inputs, ensuring user-friendly responses.
Unit Tests:
- Added tests for extreme coefficients (large and small values) to ensure robustness.
- Included existing tests to cover different edge cases.
- Invalid input tests ensure the function throws appropriate errors.
Next Steps
a. Further optimize by using Clojure's transients for performance if necessary. b. Extend the function to handle more complex scenarios, such as higher degree polynomial equations.
No hay comentarios.:
Publicar un comentario