-1

I have this assignment:

Consider a list where every element is a nested list of length 2. The first element of each nested list is either a 0 or a 1. The second element of each nested list is some integer. An example input in Scheme is written below.

'((0 1) (1 2) (1 3) (0 4) (0 3))

For the purposes of this question, let’s call the first element of each nested list the key and the second element of the nested lists the value. Now consider a function, count_by_cat, that takes such a list as input and yields a two-element list where

  • the first element is the sum of the values of all nested lists with 0 as the key, and
  • the second element is the sum of the values of all nested lists with 1 as the key

Implement count_by_cat in

  • (a) Racket, and
  • (b) ML.

It might be helpful to create helper functions. Also do not forget about map and filter (filter is not a built-in in ML).

I'm new to Racket and ML. I'm stuck at using accessing lists and stuff in Racket. Some help with ML would also be great.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • 1
    What flavor of ML? Standard? Ocaml? – Shawn Nov 08 '22 at 17:07
  • 4
    Also you'll get a better response if you include what you've tried so far. – Shawn Nov 08 '22 at 17:11
  • `filter` *is* in SML. See [here](https://smlfamily.github.io/Basis/list.html). – molbdnilo Nov 09 '22 at 06:32
  • When the instructions say that it "*may* be helpful to create helper functions" (my emphasis), it usually means that you'll get stuck pretty soon if you don't. Try to think about a smaller problem that would be a step on the way, for instance getting the sum of values for a particular key. (And "do not forget about map and filter" means "if you study them thoroughly and use them you will need to write very little code".) – molbdnilo Nov 09 '22 at 07:05

2 Answers2

1

Well you're suggested to use higher order functions like map and filter.

And indeed it is easy to manipulate our data as a whole instead of focusing, mentally, on one element at a time. Which approach (i.e. the former one) some might argue, is the essence of functional programming paradigm.

In Racket,

(define (count_by_cat ls)

"return a two-element list"

    (list
      ;; .....
      ;; ..... ))

where the first element is the sum of

      (sum-of

the values of all nested lists

        (map value

with 0 as the key

           (filter (lambda (x) (= 0 (key x))) ls)))

and the second element is the sum of the values of all nested lists

      (sum-of
        (map value

with 1 as the key

           (filter (lambda (x) (= 1 (key x))) ls))) ))

And so we need to define the helper functions accessing the nested lists,

(define (key n) (car n))

(define (value n) (cadr n))

and also the sum-of function, for summing up the numerical values in a list:

(require math/base)

(define (sum-of ls)
    (sum ls))

This was easy to write, and will be easy to maintain.

The more performant code would do everything in one pass over the input, like is shown in the other answer. It has all the logic fused into one loop body. It could be more brittle to write and maintain.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
0

The hint about map and filter is a bit of a red herring (As well as being wrong about them not being in ML languages); a helper function is very useful, but it's a fold you want, which basically calls a function with the current element of a list and an accumulator value, and uses what it returns as the accumulator for the next element and ultimately the return value of the entire fold.

For example, in ocaml, using a fold to sum a list of integers:

let sum = List.fold_left (+) 0 [ 1; 2; 3; 4; 5 ];;

((+) is the function version of the infix operator +).

Or your problem in Racket, using the for/fold comprehension, and returning two values instead of a two-element list, which is more idiomatic in Racket and Scheme (In ocaml and presumably Standard ML, you'd return a two-element tuple instead of a list, and take a list of tuples).

(define (count-by-cat lol)
  (for/fold ([zero-sum 0]
             [one-sum 0])
            ([elem (in-list lol)])
    (case (first elem)
      [(0) (values (+ zero-sum (second elem)) one-sum)]
      [(1) (values zero-sum (+ one-sum (second elem)))])))

You could also use foldl, but for/fold is often easier to use, especially when working with multiple values or other non-trivial cases.

Shawn
  • 47,241
  • 3
  • 26
  • 60