Fork me on GitHub
#off-topic
<
2022-06-26
>
yubrshen05:06:08

How can I transform the following imperative code as declarative as much as possible? I’m studying how to use declarative code (functional) to make code more readable. With the following imperative code, I’m searching for declarative expression in Clojure or Python. Starting from a seed as the last element of the list (a list of lists), I need to use the last element to generate a new element. The process should stops once the generated element matches the pattern of the decider function. The procedure to generate a new element is hard to describe. Here is the code segment to generate it:

for i in range(n):
           if last[i] >= n-1:
               last[i] = 0
           else:
               last[i] += 1
               break
       indices = indices + [last]
I can use the function of take-while with the decider function over the generated list of elements. But I have not found a sub-expression to generate an element from the last element of the previous iteration’s outcome.
import copy
def calculate(a, k, decider):
   n = len(a)
   if (k < 1 or k > n):
       raise ValueError('IllegalArgument: k.')
   seed = [0]*n # [0 for i in range(n)] equivalent
   indices = [seed]
   total = pow(n, k) -1
   for j in range(total):
       last = copy.copy(indices[ -1]) # indices[ -1] is just a reference to the same element of the list indices
       if (decider(last)):
           break
       for i in range(n):
           if last[i] >= n-1:
               last[i] = 0
           else:
               last[i] += 1
               break
       indices = indices + [last]
   return indices
indexes = calculate(['a', 'b', 'c', 'd'], 3, lambda i: (i[0] == 1 and i[1] == 1 and i[2] == 0))
print(indexes)
It should produces:
[[0, 0, 0, 0], [1, 0, 0, 0], [2, 0, 0, 0], [3, 0, 0, 0], [0, 1, 0, 0], [1, 1, 0, 0]]

Martynas Maciulevičius05:06:19

If you rely on sequential generation then you may still want to have iteration. I think for your "generate a new element" list creation you could use iterate ant take-while. And for the outer for j in range(total): you could use reduce and then terminate early using reduced . Also in Clojure you wouldn't need to do any copies yourself as it's handled for you.

yubrshen16:06:43

Thanks for the hints! Here is my attempt to rewrite in declarative expression:

(defn calculate [a k decider]
  (let [decider (fn [ [ a b c & rst]]
                  (and (= 1 a) (= 1 b) (= 0 c)))
        f (fn [lst upper]
            (if (empty? lst)
              []
              (let [ [ fst & rst] lst]
                (if (>= fst upper)
                  (concat [0]         (f rst upper))
                  (concat [(inc fst)] rst)))))
        pow (fn  [x n]
              (reduce * (repeat n x)))
        
        n (count a)
        seed (for [i (range n)] 0)
        total (pow n k)]
    (if (or
         (< k 1)
         (> k n))
      (throw (Exception. "IllegalArgument: k."))
      (reduce (fn [a v]
          (let [_last (last a)]
            (if (decider _last)
              (reduced  a)
              (conj a (f _last (dec n))))))
        [seed] (range total)))))
(calculate ['a' 'b' 'c' 'd'] 3 (fn [[fst snd thd & rest]] (and (= fst 1)
                                                              (= snd 1)
                                                              (= thd 0)))
It yields the same:
[(0 0 0 0) (1 0 0 0) (2 0 0 0) (3 0 0 0) (0 1 0 0) (1 1 0 0)] 
Thanks again! Please help to improve the code.

Martynas Maciulevičius17:06:13

I suggest to split it into two or three different functions :thinking_face:

yubrshen22:06:39

You meant take out the inline lambda function as separate ones outside of the function calculate?

mauricio.szabo01:06:40

@U2QGRCMSM I actually didn't understand what the code should do. Can you explain what it's expected of it, so maybe I can show an option tomorrow?

Martynas Maciulevičius04:06:35

I have no idea what the function does but what I DO know is that all functions in your let definition should be defined separately and have proper names. And you have three there.

yubrshen03:06:35

Thanks! I'll try to give them names and move them outside to make it more readable.

Martynas Maciulevičius03:06:15

The more names the better. A good name is add-numbers or take-out-the-trash or fly-rockets.

šŸ‘ 1
yubrshen03:06:00

(defn decider
  "Determine if the expected element found?"
  [ [ a b c & rst]]
  (and (= 1 a) (= 1 b) (= 0 c)))

(defn from-last-element
  "Generate a new element from the last element of the existing list."
  [lst upper]
  (if (empty? lst)
    []
    (let [ [ fst & rst] lst]
      (if (>= fst upper)
        (concat [0]         (f rst upper))
        (concat [(inc fst)] rst)))))

(defn pow
  "Power of x to the nth"
  [x n]
  (reduce * (repeat n x)))

(defn calculate [a k decider]
  (let [n (count a)
        seed (for [i (range n)] 0)
        total (pow n k)]
    (if (or
         (< k 1)
         (> k n))
      (throw (Exception. "IllegalArgument: k."))
      (reduce (fn [a v]
          (let [_last (last a)]
            (if (decider _last)
              (reduced  a)
              (conj a (from-last-element _last (dec n))))))
        [seed] (range total)))))
(calculate ['a' 'b' 'c' 'd'] 3 (fn [[fst snd thd & rest]] (and (= fst 1)
                                                              (= snd 1)
                                                              (= thd 0))))
My challenge is that the code is not very straightforward, that it's hard to give them descriptive names.

Martynas Maciulevičius16:06:44

Why would one want to host his website on port 80 without SSL sertificate? I don't understand. Should it then 301 the user into the https version? Edit: Today I ran an app on aws and well... port 80. There is no certificate and anything. They didn't do it out of the box. Maybe it makes sense if I'd want to use CloudFront and then AWS would have unencrypted traffic inside but then it's still unencrypted in one network segment. So I'm puzzled why is it the default then...

šŸ‘ 1
Dimitar Uzunov17:06:17

What did you use to host the app? AWS is a collection of building blocks and some services that configure them

Martynas Maciulevičius18:06:30

I currently making my certificate work. Found that I had to make the HTTP load balancer return a redirect to HTTPS. And I'll also create a cerfiticate.

Martynas Maciulevičius18:06:39

> wget <http://_>._._/ -v

--2022-06-26 21:24:39--  <http://_/>
Resolving _ (_)... _, _
Connecting to _ (_)|_|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: <https://_:443/> [following]
--2022-06-26 21:24:39--  <https://_/>
Loaded CA certificate '/etc/ssl/certs/ca-certificates.crt'
Connecting to _ (_)|_|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2211 (2.2K) [text/html]
Saving to: 'index.html'

index.html                                                  100%[==========================================================================================================================================>]   2.16K  --.-KB/s    in 0s      

2022-06-26 21:24:39 (33.5 MB/s) - 'index.html' saved [2211/2211]
So here we go. I manage to make a redirect. I didn't know that the LB could support the certificate :thinking_face:

Dimitar Uzunov18:06:11

If you are hosting on a single instance you can use something like nginx and let's encrypt for your certificate

Martynas Maciulevičius18:06:08

Currently I start with one instance that does too much stuff and then I'll probably need to split it into several instance types. So I don't want to commit yet.

šŸ‘ 1
Ben Sless19:06:27

What if it's a static HTML page? If it's simple and doesn't send or receive data, who cares?

1
Martynas Maciulevičius20:06:32

But even if it's HTML page that you can interact with and that doesn't submit data to your backend... it could be a smart-contract page and then it's better to serve it through HTTPS :thinking_face: And then if it's your portfolio page then it's not nice if you get warnings from browser :thinking_face:

p-himik20:06:10

HTTPS makes sense even for web 1.0 HTML pages. I'm 90% certain that there have been cases of internet providers incorporating ads into plain HTTP HTML pages - exactly because HTTP does not prevent any MitM attacks.

šŸ‘† 1
😬 1
Ben Sless03:06:21

That sounds... Terrifying

lsenjov04:06:34

Only a little MitM attack, as a treat

Ben Sless05:06:17

Is it possible if the website has no js?

lsenjov05:06:17

Yes, because it's modifying the bare HTML

lsenjov05:06:38

So it's fairly trivial if you've got the MitM to inject <script> tags

Martynas Maciulevičius05:06:51

If you configure your browser to run that page without JS they still can include static HTML but then the scripts won't run. But then they can even show "enable JS for full functionality" instead of the original so that you would enable the JS for them. Which would mean they could run the scripts anyway.

p-himik06:06:45

No need for JS at all. Just inject a bunch of <p>Your PC is infected, <a href="...">CLICK HERE</a> to fix it</p>.

lsenjov06:06:06

Change all links to be scam links

lsenjov06:06:19

Or better, change the entire page to be an invisible <a> tag

Martynas Maciulevičius06:06:28

If you want to do this then why not return a 302 redirect instead of the content of the HTML... Way simpler.

p-himik06:06:39

Perhaps. But in any case, the point is that HTTPS is needed almost everywhere. :) Regardless of the kind of the content. "Almost" because you can use third-party things to check the content, like e.g. it's going on with HTTP Debian repositories.