Fork me on GitHub
#off-topic
<
2022-12-05
>
adi10:12:57

Data nerds here may enjoy Hanukkah of Data[1], starting Dec 18th. It is a data game composed of a series of 8 free data puzzles, tied together with a whimsical narrative. Inspired by Advent of Code, HoD is being made by Saul Pwanson (of Visidata fame)[2] along with fellow gentlenerds at devottys [3] and beyond (I'm helping too). 🀓 😄 There is a newsletter sign-up at the game page[1] if you would like a reminder when the puzzles are live. =) [1] Hanukkah of Data! https://hanukkah.bluebird.sh/ [2] Inspired by Advent of Code: https://www.visidata.org/blog/2022/hanukkah-of-data/ [3] devottys https://bluebird.sh/about/

gratitude-thank-you 3
3
quoll14:12:26

Over the weekend I tried using ChatGPT, posting the https://4clojure.oxal.org/#/problem/73 without modification. It took some coaching to get it through, and I forgot to capture the first description, but I found it interesting 🧵

quoll15:12:25

Without anything other than copy/paste from the problem, it returned:

(defn tic-tac-toe-winner [board]
  (let [n (count board)
        horizontal-wins (for [i (range n)]
                          (if (= (set (board i)) #{:x}) :x
                              (if (= (set (board i)) #{:o}) :o)))
        vertical-wins (for [i (range n)]
                        (if (= (set (mapv (fn [row] (nth row i)) board)) #{:x}) :x
                            (if (= (set (mapv (fn [row] (nth row i)) board)) #{:o}) :o)))
        diagonal-wins (for [i [0 n]]
                        (if (= (set (mapv (fn [row] (nth row i)) board)) #{:x}) :x
                            (if (= (set (mapv (fn [row] (nth row i)) board)) #{:o}) :o)))
        wins (concat horizontal-wins vertical-wins diagonal-wins)]
    (first wins)))

quoll15:12:34

Me: > Each of the for loops are emitting values for every iteration, including when those values are nil. Each of the values horizontal-wins, vertical-wins and diagonal-wins contains multiple nil values. The final concatenated sequence of wins also contains multiple nil values. This means that the final (first wins) expression will usually return nil instead of the correct answer Here is a further revised implementation of the function that fixes the errors in the previous code:

(defn tic-tac-toe-winner [board]
 (let [n (count board)
    horizontal-wins (for [i (range n)]
             (if (= (set (board i)) #{:x}) :x
               (if (= (set (board i)) #{:o}) :o))
             :else nil)
    vertical-wins (for [i (range n)]
            (if (= (set (mapv (fn [row] (nth row i)) board)) #{:x}) :x
              (if (= (set (mapv (fn [row] (nth row i)) board)) #{:o}) :o))
             :else nil)
    diagonal-wins (for [i [0 n]]
            (if (= (set (mapv (fn [row] (nth row i)) board)) #{:x}) :x
              (if (= (set (mapv (fn [row] (nth row i)) board)) #{:o}) :o))
             :else nil)
    wins (filter #(not (nil? %)) (concat horizontal-wins vertical-wins diagonal-wins))]
  (first wins)))
In this revised version, the for loops are using the :else clause to ensure that nil values are not emitted for any of the iterations. The final concat and first expressions are wrapped in a filter call to remove any remaining nil values from the sequence of wins, ensuring that the correct result is returned.

quoll15:12:57

Me: > The use of :else expressions is a third argument in the for macro, which is an error. Instead, the third argument in an if macro contains the “else” value to return when the test in the if returns false. When an if macro does not specify a value to return, then a nil will be returned. Here is a further revised implementation of the function that fixes the errors in the previous code:

(defn tic-tac-toe-winner [board]
 (let [n (count board)
    horizontal-wins (for [i (range n)]
             (if (= (set (board i)) #{:x}) :x
               (if (= (set (board i)) #{:o}) :o))
    vertical-wins (for [i (range n)]
            (if (= (set (mapv (fn [row] (nth row i)) board)) #{:x}) :x
              (if (= (set (mapv (fn [row] (nth row i)) board)) #{:o}) :o))
    diagonal-wins (for [i [0 n]]
            (if (= (set (mapv (fn [row] (nth row i)) board)) #{:x}) :x
              (if (= (set (mapv (fn [row] (nth row i)) board)) #{:o}) :o))
    wins (filter #(not (nil? %)) (concat horizontal-wins vertical-wins diagonal-wins))]
  (first wins)))
In this revised version, the if macros are used correctly, with a test expression and a value to return if the test is true. Because the if macros do not specify a value to return if the test is false, they will return nil by default. This ensures that the function will return the correct result for each possible input.

quoll15:12:43

Me: > Each use of the for macro has unbalanced parentheses. An extra closing parenthesis at the end of each for macro will fix this Here is a final implementation of the function that fixes the errors in the previous code:

(defn tic-tac-toe-winner [board]
 (let [n (count board)
    horizontal-wins (for [i (range n)]
             (if (= (set (board i)) #{:x}) :x
               (if (= (set (board i)) #{:o}) :o)))
    vertical-wins (for [i (range n)]
            (if (= (set (mapv (fn [row] (nth row i)) board)) #{:x}) :x
              (if (= (set (mapv (fn [row] (nth row i)) board)) #{:o}) :o)))
    diagonal-wins (for [i [0 n]]
            (if (= (set (mapv (fn [row] (nth row i)) board)) #{:x}) :x
              (if (= (set (mapv (fn [row] (nth row i)) board)) #{:o}) :o)))
    wins (filter #(not (nil? %)) (concat horizontal-wins vertical-wins diagonal-wins))]
  (first wins)))
In this final version, the for macros are used correctly, with balanced parentheses and properly nested if statements. The function will return the correct result for each possible input, and is ready to be used in a Clojure program.

quoll15:12:24

That final one worked. I was surprised that it attempted a solution with unbalanced parentheses. And the first version looked like someone who knew imperative languages and expected Clojure’s if statements to operate that way. But it revised quickly and effectively.

quoll15:12:18

If it had access to the specification of the language, and could continue to learn, then I think it would become a useful tool to build basic systems

quoll15:12:45

I tried to stick to explaining what it was doing wrong, rather than leading it to another solution. I didn’t think it was a well coded solution, but the approach was fine.

👍 1
Martin Půda19:12:26

ChatGPT- Clojure coding exercises. More in 🧵

Martin Půda19:12:56

Prompt: Create five coding exercises for learning Clojure. Write a function that takes a list of numbers as input and returns the sum of the numbers in the list. Write a function that takes a list of strings as input and returns a new list with all of the strings converted to uppercase. Write a function that takes a list of numbers and a number as input and returns a new list containing only the numbers from the original list that are greater than the specified number. Write a function that takes a list of numbers as input and returns a new list containing only the even numbers from the original list. Write a function that takes a list of numbers and a number as input and returns a new list containing only the numbers from the original list that are multiples of the specified number. Prompt: Create five more. Write a function that takes a string as input and returns a new string with all of the vowels removed. Write a function that takes a string as input and returns a new string with the characters in reverse order. Write a function that takes a list of strings and a string as input and returns a new list containing only the strings from the original list that contain the specified string. Write a function that takes a list of numbers and a number as input and returns the index of the first occurrence of the specified number in the list, or -1 if the number is not present in the list. Write a function that takes a list of numbers as input and returns a new list containing the square of each number in the original list. Prompt: Create five difficult coding exercises for learning Clojure. Write a function that takes a list of numbers as input and returns a new list containing only the numbers from the original list that are perfect squares and also prime numbers. Write a function that takes a list of strings and a string as input and returns a new list containing only the strings from the original list that are anagrams of the specified string. Write a function that takes a list of numbers as input and returns a new list containing only the numbers from the original list that are prime, perfect squares, and also palindromic (i.e. the number is the same forwards and backwards, such as 11 or 121). Write a function that takes a list of numbers as input and returns a new list containing the prime factorization of each number in the original list. For example, if the input list is [10, 15, 20], the output list should be [[2, 5], [3, 5], [2, 2, 5]]. Write a function that takes a list of numbers as input and returns a new list containing the prime factorization of each number in the original list, but only using the prime numbers that are also palindromic. For example, if the input list is [10, 15, 20], the output list should be [[11], [101], [2, 11]]. Prompt: Create five coding exercises for learning Clojure's function "reduce". Write a function that uses the reduce function to sum a list of numbers. Write a function that uses the reduce function to concatenate a list of strings into one string. Write a function that uses the reduce function to calculate the product of a list of numbers. Write a function that uses the reduce function to find the maximum number in a list of numbers. Write a function that uses the reduce function to find the minimum number in a list of numbers.

phronmophobic19:12:29

Can it also write tests for you to check your solution?

phronmophobic19:12:37

that would be neat.

Martin Půda20:12:00

It seems that you can just ask whether your solution is correct 😆 Prompt: Give me one exercise for learning Clojure. One exercise for learning Clojure could be to write a function that takes a list of numbers and a number as input, and returns a new list containing only the numbers from the original list that are greater than the specified number. The function could be called "filter-greater-than" and it could be used like this: (filter-greater-than [1 2 3 4 5] 3) ;; => (4 5) To solve this exercise, you would need to use the Clojure function "filter" to create a new list containing only the numbers from the original list that are greater than the specified number. You would also need to use the Clojure function ">" to compare each number in the list to the specified number and determine whether it should be included in the new list. Prompt: Is this solution correct? (defn filter-greater-than [xs x] (filter #(&gt; % x) xs)) Yes, this solution is correct. It defines a function called "filter-greater-than" that takes two arguments: a list of numbers called "xs" and a number called "x". The function uses the "filter" function to create a new list containing only the numbers from the original list that are greater than the specified number. It uses the ">" function to compare each number in the list to the specified number and determine whether it should be included in the new list. Here is an example of how this function could be used: (filter-greater-than [1 2 3 4 5] 3) ;; => (4 5) In this example, the function is called with a list of numbers [1 2 3 4 5] and the number 3. The function uses the "filter" function to create a new list containing only the numbers from the original list that are greater than 3 (i.e. the numbers 4 and 5). The result of calling the function is the new list (4 5).

bherrmann23:12:03

Yea, this ChatGPT is pretty freaky.

bob> Can you give me an example using tagsoup to parse an html string and extract the attributes "href" and "src" from the elements "a" and "img" so that the urls can be checked for correctness?

ChatGPT> Sure, here is an example of using the clj-tagsoup library to parse an HTML string and extract the "href" and "src" attributes from the "a" and "img" elements:
(ns my-app.core
  (:require [clj-tagsoup.core :as tagsoup]
            [ :as io]))

(defn parse-html [html-str]
  (with-open [input-stream (io/input-stream html-str)]
    (->> (tagsoup/parse input-stream)
         (tagsoup/select ["a" "img"])
         (map (fn [node]
                (select-keys node [:attrs])))))

(defn get-urls [html-str]
  (->> (parse-html html-str)
       (map :attrs)
       (mapcat (juxt :href :src))
       (remove nil)))

(defn check-urls [html-str]
  (let [urls (get-urls html-str)]
    (map (fn [url]
           (if (valid-url? url)
             url
             (println (str "Invalid URL: " url))))
         urls)))

This code uses the parse function from the clj-tagsoup.core namespace to parse the HTML string and return a sequence of nodes. It then uses the select function to select only the "a" and "img" elements, and the map function to extract the "attrs" map from each node. The get-urls function uses the mapcat function to extract the "href" and "src" attributes from the "attrs" maps, and the remove function to remove any nil values (in case an element does not have a "href" or "src" attribute).

The check-urls function uses the valid-url? function (which is not shown in the code above) to check if each URL is valid, and prints any invalid URLs to the console. You can implement the valid-url? function in any way you choose, depending on your specific requirements for what constitutes a valid URL.

I hope this helps! Let me know if you have any other questions.

mr-burns-excellent 1
1