Convert to Racket(Scheme)

Question:

I have this code that works, on python. I am new to DRrACKET, how can I translate it into DRrACKET.
I am struggling to write a DRrACKET code that produces the exact result.

Your help is highly appreciated.

# python program to find the length of the largest subarray which has all contiguous elements 

def length_sub_(arr, n):
max_len = 1
for i in range(0,n - 1):

    myset = set()
    myset.add(arr[i])

    # Initialize max and min in
    # current subarray
    mn = arr[i]
    mx = arr[i]
    for j in range(i + 1,n):

        # If current element is already
        # in hash set, then this subarray
        # cannot contain contiguous elements
        if arr[j] in myset:
            break


        # Else add current element to hash
        # set and update min, max if required.
        myset.add(arr[j])
        mn = min(mn, arr[j])
        mx = max(mx, arr[j])

        # We have already checked for
        # duplicates, now check for other
        #property and update max_len
        # if needed
        if mx - mn == j - i:
            max_len = max(max_len, mx - mn + 1)

 return max_len # Return result



arr = [6, 1, 1, 10, 10, 111, 100]
print("Length of the longest contiguous subarray is : ",
                            length_sub_(arr,len(arr)))

Expected output:

The length of the longest contiguous subarray is : 2

My DRrACKET start
I have have implemented some custom functions that acts as helper functions to the output am expecting

 #lang racket
 ;custom function to check if an elemet is contained in a list
 (define custom-member
  (lambda (x los)
    (cond
     ((null? los) #f)
      ((= x (car los)) #t)
      (else (custom-member x (cdr los))))))

    ;checks if all the elements in the set are unique
    (define are-all-unique 
      (lambda (v)
       (if (not (null? v))
        (and (not (custom-member (car v) (cdr v)))
         (are-all-unique (cdr v)))
         #t)))
   ;reverses the list
    (define (reverse-helper lst ACC)
     (if (null? last)
     acc
  (reverse-helper (cdr lst) (cons (car lst) ACC))))

 (define (custom-reverse last)
   (reverse-helper lst '()))

(define (unique-elements last)
  (let loop ((lst (flatten lst)) (res '()))
  (if (empty? last)
     (custom-reverse res)
     (let ((c (car lst)))
      (loop (cdr lst) (if (custom-member c res) res (cons c     res)))))))


; define the length of the list
 (define custom-length
  (lambda (list)
   (if (null? list)
     0
    (+ 1 (custom-length (cdr list))))))


;performs the actual list check
(define max-contiguous-repeated-length
 (lambda (L)
  (cond
   [(null? L) 0]
    [else (cond
           [(are-all-unique L) 1]
           [else (custom-length (unique-elements L))])]
    )
   ))

  (max-contiguous-repeated-length '(1 1 2 1 2 1 2))
Asked By: Calvince

||

Answers:

Attempting to "translate" from Python is probably not the best approach when learning Scheme/Racket; try starting with this
stub, and following the Racket
How To Design Functions
design recipe:

(define (max-repeated-length lon) ;; ListOfNumber -> Natural
  ;; produce length of longest section of lon with equal elements
  0)

(check-expect (max-repeated-length '()) 0)

; (check-expect (max-repeated-length '(6 1 1 10 10 111 100)) 2)

The next step in the design method is to choose a template,
replace generic identifiers with ones appropriate for the required function,
and add examples to guide the coding of replacements for placeholders in the template.

The simplest template for functions with a list argument is "natural recursion":

(define (fn lox) ;; ListOfX -> Y                  ; *template*: (for fn with list argument)
  ;; produce a Y from lox using natural recursion ;
  (cond                                           ;
    [(empty? lox) ... ]                           ; (... = "base case value" ;; Y )
    [else (...                                    ; (... = "inventory fn(s)" ;; X Y -> Y )
           (first lox) (fn (rest lox))) ]))       ; [1]

Edited for this problem, this would be:

(define (max-repeated-length lon) ;; ListOfNumber -> Natural
  ;; produce length of longest section of lon with equal elements
  (cond
    [(empty? lon) 0 ]
    [else (...
           (first lon) (max-repeated-length (rest lon))) ]))

so this is how one could start to elaborate (define max-repeated-length (lambda (L) (cond ) ) ).

Examples could be:

(check-expect (max-repeated-length '(1)) 1)
(check-expect (max-repeated-length '(1 2)) 1)
(check-expect (max-repeated-length '(1 2 2)) 2)
(check-expect (max-repeated-length '(1 2 2 2 3 3)) 3)

The template above is a start (the first test passes), but it may not be clear how to
start coding the replacement for ....
How to Design Functions
has relevant design recipes and templates, but this answer will not use those explicitly.

The issue with proceeding with the natural recursion template is that the result to be
produced depends on lengths of repeat sections within lon. With programming experience,
one can see that the current value being repeated (cur-val),
the length-so-far of current repeat (cur-lsf), and the maximum-so-far
of repeat lengths (max-lsf), are needed to generate the required value.
max-repeated-length does not incorporate these values, so introduce a function with
them as arguments ( (max-repeats lon cur-val cur-lsf max-lsf) ), and call it from max-repeated-length, which becomes:

(define (max-repeated-length lon) ;; ListOfNumber -> Natural
  ;; produce length of longest section of lon with equal elements
  (cond
    [(empty? lon) 0]
    [else (max-repeats (rest lon) (first lon) 1 1) ]))

(the arguments to max-repeats are deduced from the fact that cur-val starts as (first lon), etc)

The max-repeats stub, with signature, purpose, and stub value can now be written,
with some tests:

(define (max-repeats lon cur-val cur-lsf max-lsf) ;; ListOfNumber Number Natural Natural -> Natural
  ;; produce max-so-far of lengths of sections of lon with equal elements
  max-lsf)
  
(check-expect (max-repeats '() 1 1 1) 1)
(check-expect (max-repeats '(2) 1 1 1) 1)
(check-expect (max-repeats '(2) 2 1 1) 2)

max-repeats can be templated like natural recursion with an extra condition for when a repeat is found:

(define (max-repeats lon cur-val cur-lsf max-lsf) ;; ListOfNumber Number Natural Natural -> Natural
  ;; produce max-so-far of lengths of sections of lon with equal elements
  (cond
    [(empty? lon) max-lsf ]
    [(= (first lon) cur-val) ... ]
    [else ... ]))

(the placeholders will be replaced by forms built from the arguments,
(first lon), (max-repeats (rest lon) ... ), and (add1 cur-lsf))

The else ... placeholder is simple: it’s the start of a (possible) new repeat, so like
the call in max-repeated-length but keeping the max-lsf value. Looking at the examples,
one can see that the full max-repeats, with max-repeated-length as before,
and another test, is:

(define (max-repeats lon cur-val cur-lsf max-lsf) ;; ListOfNumber Number Natural Natural -> Natural
  ;; produce max-so-far of lengths of sections of lon with equal elements
  (cond
    [(empty? lon) max-lsf ]
    [(= (first lon) cur-val)
     (max-repeats (rest lon) cur-val (add1 cur-lsf) (max max-lsf (add1 cur-lsf))) ]
    [else (max-repeats (rest lon) (first lon) 1 max-lsf) ]))

(define (max-repeated-length lon) ;; ListOfNumber -> Natural
  ;; produce length of longest section of lon with equal elements
  (cond
    [(empty? lon) 0]
    [else (max-repeats (rest lon) (first lon) 1 1) ]))

(check-expect (max-repeated-length '(1 2 3 3 4 5 5 5 6 7 7 8)) 3)

So, running again to check:

Welcome to DrRacket, version 8.4 [cs].
Language: Beginning Student with List Abbreviations [custom].
Teachpack: batch-io.rkt.
All 10 tests passed!
> 

Note that if = is replaced by equal? [2], max-repeated-length may be applied to
lists of Scheme objects of many kinds, eg:
(max-repeated-length ‘(a b b b c c)) ==> 3
(max-repeated-length ‘(1 ‘(2 3) ‘(2 3) ‘(2 3) 4 4)) ==> 3

[1]: the template (... (first lox) (fn (rest lox))) may indicate a more elaborate form incorporating (first lox) and (rest lox)

[2]: (Other equivalence predicates are available)

Answered By: mnemenaut

One nice thing about Scheme is that it encourages you to look for elegant solutions rather than the creeping horror in the question, safe in the knowledge that the elegant solution will generally also be the efficient one.

So, since this is, I think, rather obviously a homework problem and I don’t really want to help people cheat, here is an elegant solution in Python. In Python this will perhaps not be efficient, especially for long arrays. Turned into Racket it is both the obvious solution and efficient.

def longest_contiguous(a):
    l = len(a)
    def loc(i, current, this, best):
        # i is current index, current is the element in the current
        # run, this is the length of the current run, best is the
        # length of the longest run.
        if i == l:              # we're done
            return best if best > this else this
        elif a[i] == current:   # in a run
            return loc(i + 1, current, this + 1, best)
        else:                   # new run
            return loc(i + 1, a[i], 1, best if best > this else this)
    return loc(0, a[0], 1, 1) if l > 0 else 0

And here is such an elegant solution in Racket: framed in such a way that, again, it probably can’t be submitted as homework. This will work on both vectors and lists, and indeed many other things. And as well as the length of the longest contiguous sequence, it will tell you where that sequence starts.

(require racket/stream
         racket/generic)

(define-generics to-stream
  ;; Turn something into a stream in a way which could be extended
  (->stream to-stream)
  #:fast-defaults
  ((stream?
    (define ->stream identity))
   (sequence?
    (define ->stream sequence->stream))))

(define (length-of-longest-contiguous-subthing thing
                                               #:same? (same? eqv?)
                                               #:count-from (count-from 0))
  (define s (->stream thing))
  (if (stream-empty? s)
      (values 0 count-from)
      (let los ([st (stream-rest s)]
                [current (stream-first s)]
                [count (+ count-from 1)]
                [this 1]
                [this-start 0]
                [best 0]
                [best-start 0])
        (cond
          [(stream-empty? st)
           (if (> this best)
               (values this this-start)
               (values best best-start))]
          [(same? (stream-first st) current)
           (los (stream-rest st) current (+ count 1)
                (+ this 1) this-start
                best best-start)]
          [else
           (if (> this best)
               (los (stream-rest st)
                    (stream-first st) (+ count 1)
                    1 count
                    this this-start)
               (los (stream-rest st)
                    (stream-first st) (+ count 1)
                    1 count
                    best best-start))]))))

Now, for instance:

> (length-of-longest-contiguous-subthing '(1 2 3 4 4 5 6 6 6 7))
3
6

The longest contiguous subsequence is 3 elements log and starts at the 6th element.

But also:

> (call-with-input-file
   "/tmp/x"
   (λ (p)
     (length-of-longest-contiguous-subthing (in-lines p)
                                            #:same? string=?
                                            #:count-from 1)))

The longest sequence of identical lines in /tmp/x is 2, and it starts at line 2, counting from 1.

Answered By: ignis volens

OK so you want,

[6, 1, 1, 10, 10, 111, 100] => 2
[6, 1, 1, 1, 100, 111, 100] => 3
[6, 10, 10, 20, 10, 10, 10] => 3

because

[[6], [1, 1], [10, 10], [111], [100]] => [1,2,2,1,1] => 2
[[6], [1, 1, 1], [100], [111], [100]] => [1,3,1,1,1] => 3
[[6], [10, 10], [20], [10, 10, 10]]   => [1,2,1,3]   => 3

And so, taking the higher-level view, we

(define (length-longest-constant-span xs equ)
  (maximum 
    (map length
      (constant-spans xs equ))))

Now, maximum is easy to

(define (maximum lon)
   (apply max lon))

and we’re left with the task of

(define (constant-spans xs equ)

for xs which can be either empty

  (if (null? xs)
    (list (list))     ; [[]]

(returning [[]] because [1] => [[1]] and so it should be [] => [[]]); or non-empty,

    (let* ((a (car xs))
           (d (cdr xs))

and since we’re coding it up, we’ll finish writing it sometime, and then it will be available to us to call so we might as well call it, with a smaller part of the input,

           (r (constant-spans d equ))

and then we’ll be able to analyze the recursive result, which we know will never be empty — because of how we’ve just defined the handling of the empty list case above, returning the non-empty list [[]]

           (ra (car r))
           (rd (cdr r)))

(or we could’ve repeated the same if ... null? ... let* ... car ... cdr ... dance…)(*)

so that now, finally, we get to decide what to do with that recursive result, case by case, whether the head element ra in it is empty,

      (cond
        ((null? ra)   ; [[]] --> [[a]]
          (cons (cons a ra) rd))

or non-empty, so we can peek into it, and see whether its head element is the same as a, as deemed by equ,

        ((equ a (car ra))  ; a [[a,...],...] --> [[a,a,...],...]
          (cons (cons a ra) rd))

or not (it’s same thing we’re doing here as above, huh. So we can simplify the code if we want, later; but first,):

        (else              ; a [[b,...],...] --> [[a],[b,...],...]
          (cons (list a) r))))))

and that’s that.

If I made any errors here, do kindly fix them, to make it work.


(*) which combination can be seen as one primitive pattern-based data handling operation — sometimes known as caseof — under the data type oriented paradigm. caseof combines data case detection, analysis, and binding of variables.

In such pseudocode a data analyzing code naturally follows the data type definition:

type | [a, ..[b, ...]]
     | []
.......
rle xs equ =
  caseof xs
  | [] --> [[]]
  | [a, ..d] --> 
    caseof (rle d equ)
    | [] --> ...
    | [ra, ..rd] --> ...
........

making the decision tree apparent in our code.

Answered By: Will Ness
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.