Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - hartrock

#106
Anything else we might add? / Re: apply for arrays?
November 17, 2013, 06:57:44 PM
Quote from: "rickyboy"If the input "sequence" is an array, what would the output "sequence" be -- an array or list?

For map the output naturally would be an array (for apply applied func decides).



A multi-dimensional array can be seen as an array of arrays: then map would just map first dimension.

This seems to be the semantics of array; at least it's possible to replace elements of first dim by both lists and arrays, without changing the type of the array:
> (setq a (array 2 5 (sequence 0 9)))
((0 1 2 3 4) (5 6 7 8 9))
> (setq (a 0) (sequence 10 14))
(10 11 12 13 14)
> (setq (a 1) (array 5 (sequence 15 19)))
(15 16 17 18 19)
> (array? (a 0))
nil
> (array? (a 1))
true
>> (array? a)
true
>


In case of mapping all lowest-level elements - e.g. pixels in an image - map had to be nested.
#107
Anything else we might add? / apply for arrays?
November 16, 2013, 02:33:15 PM
[update] -> 10.5.6.



(setq a (array 10 (sequence 1 10)))

;; currently we have:
(setq a_applied (apply + (array-list a)))

;; nice would be:
(setq a_applied (apply-array + a))
;; or:
(setq a_applied (apply + a))

Func apply-array could work at source array directly (without indirection by conversions to/from lists).

Because
 

[*] it shouldn't be more expensive than its list counterpart,
  • [*] the result type depends on to be applied function and not on source to be applied to (array);
  • [/list]

    it probably could be named the same (and internally just do the right thing).
    #108
    newLISP in the real world / Re: PS doc changes
    November 16, 2013, 10:19:13 AM
    Appearing twice (two text lines in between):
    (myarray 0 -1)      → 4
    #109
    newLISP in the real world / [fix][bug] (round 0.5) -> 0
    November 15, 2013, 05:52:07 PM
    Fix:

    sr@free:~/tmp/newlisp-10.5.4$ diff -u nl-math.c_orig nl-math.c
    --- nl-math.c_orig      2013-10-01 17:52:03.000000000 +0200
    +++ nl-math.c   2013-11-16 02:49:02.000000000 +0100
    @@ -717,7 +717,7 @@
     if(params != nilCell)
         getInteger(params, (UINT*)&digits);
     
    -if(digits > 0)
    +if(digits >= 0)
         {
         precision = pow(10.0, (double)(digits > 20 ? 20 : digits));
         fNum = precision * floor(fNum/precision + 0.5);
    #110
    newLISP in the real world / [bug[fix]] (round 0.5) -> 0
    November 15, 2013, 05:14:28 PM

    > (round 5 1)
    10
    > (round 0.5)
    0
    > (round 0.05 -1)
    0.1

    Seems to be a bug: at least not consistent.



    My version: newLISP v.10.5.4 64-bit on Linux IPv4/6 UTF-8 libffi.
    #111
    Quote from: "Lutz"To summarize: the only reason for newLISP doing unwanted command line processing, is programmer error.  

    This is not the only reason: there is also the interest in interactive development: no error, but wishing to inspect things from inside of the interpreter after the script code has run.


    Quote from: "Lutz"
    There is no other normal use case for a built-in mechanism to stop processing the command line. If processing occurs and is unwanted, it always means either a code error or a missing exit statement during code development and for debugging. Somehow the program lost control and finishes at the top level without exiting, causing newLISP to continue processing command line parameters.

    See above.


    Quote from: "Lutz"
    To help debugging in future versions, a simple (reset) will also interrupt command line processing and enter interactive mode. No error handler will be required. So this will do it too:



    #!/usr/bin/newlisp

    (module "getopts.lsp")

    (shortopt "p" (println "players: " getopts:arg) "int" "num of players")
    (shortopt "h" (println (getopts:usage)) nil "usage")

    (getopts (2 (main-args)))

    (reset) ; stop processing - now also including command line parsing

    (exit) ; <--- never gets here because reset interrupts processing



    I like this solution!

    (reset) with error handler already does exactly what I want: the script runs and afterwards it's possible to inspect symbols and their contents in the interpreter.



    Without error handler there are problems:

    [*] without (reset) it hangs at an unknown opt - e.g. '-c' - this is my use case -,
  • [*] with (reset) the interpreter exits.
  • [/list]


    Thanks for your will - and work - to improve this!

    After this change it's just a change of (exit) to (reset) and vice-versa (just to change one semicolon for commenting out/in (reset)).


    Quote from: "Lutz"
    Ps: A scripting language checking the execution bit would be very unusual, no scripting language does that.

    OK: thanks for the info.



    Moreover I think now, that the (reset) mechanism:

    [*] has lower overhead - no need to check file status after loading it -, and
  • [*] it gives less suprises - with checking exec bit of the file, it depends from the position of it, if and when CL arg parsing stops (the (reset) mechanism is cristal clear in this respect: stop CL arg parsing, if developer explicitely says so).
  • [/list]


    Note: I have changed the title of my original post away from [FR} (feature request).
    #112
    Thanks for the hint: this is a solution. I even haven't thought about exceptions for this use case, though some assert macros of mine use them, too.

    Though there is more to do for the developer than just to comment out (exit); for debugging this should be fine.



    What about having some kind of non-error exception for this use case?

    Because there needn't be an error, it's just to keep the interpreter off from parsing of command line args and entering it instead for an interactive session.



    [4. update]: Removed some stuff about a bad idea of checking the exec bit of interpreter loaded scripts for stopping interpreter CL parsing.
    #113
    newLISP in the real world / [4. update] Removed.
    November 13, 2013, 11:24:18 PM
    ...
    #114
    .. with a simple patch for a first solution and further ideas for possibly better ones...

    [4. update] Removed some stupid patch ideas.

    [3. update]

    Solution for 10.5.4: http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4416#p21792">http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4416#p21792

    Solution for later versions: http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4416#p21794">http://www.newlispfanclub.alh.net/forum/viewtopic.php?f=16&t=4416#p21794

    '[HowTo]' added to title.

    [2. update] Changed title away from feature request: some discussion revealed a very good solution (see posts thereafter); and the patches are not a good idea compared with it.

    [update] Better patch - IMHO - in self-reply to this post.



    Doing more and more scripting with newLISP, it's important to me to have interactive debugging facilities.

    Furthermore it's good to have some separation between CLI args targeted to the interpreter and others targeted to a script interpreted by it.



    Assumed there is the following code:

    #!/usr/bin/newlisp

    (module "getopts.lsp")

    (shortopt "p" (println "players: " getopts:arg) "int" "num of players")
    (shortopt "h" (println (getopts:usage)) nil "usage")

    (getopts (2 (main-args)))

    ;; some code here: if something goes wrong, or just for interactive development
    ;; no (exit) could be a good idea here...
    (exit)

    This gives:

    sr@free:~/NewLisp$ ./game.lsp -p 10
    players: 10
    sr@free:~/NewLisp$

    So far so good.



    But if something goes wrong, or we just want to inspect some symbols before (exit), commenting out (exit) does not work:

    #!/usr/bin/newlisp

    (module "getopts.lsp")

    (shortopt "p" (println "players: " getopts:arg) "int" "num of players")
    (shortopt "h" (println (getopts:usage)) nil "usage")

    (getopts (2 (main-args)))

    ;; some code here: if something goes wrong, or just for interactive development
    ;; no (exit) could be a good idea here...
    ;(exit)

    We get:

    sr@free:~/NewLisp$ ./game.lsp -p 10
    players: 10
    newLISP server setup on 10 failed.
    sr@free:~/NewLisp$

    instead of entering newLISP interpreter.



    [4. update]: Removed some stupid patch ideas.



    What do you think?
    #115
    To illustrate Lutz' description of the read/compile/evaluate process some session:

    newLISP v.10.5.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    >
    ;; # (1) (2) ..
    (define (create-funcs)
      (println "in create-funcs")
      (context 'C)
      (define (f_1) 1)
      (context MAIN))
    ;; .. (1) (2) #
    ;;
    "(dolist (s (symbols) (= (string s) "f_1")))"
    (dolist (s (symbols) (= (string s) "f_1")))
    "-> 'f_1 symbol exists in MAIN, but .."
    ;;
    "f_1"
    f_1
    "-> .. is nil: function not defined yet"
    ;;
    ;; # (3) (4) ..
    "(create-funcs)"(create-funcs)
    ;; .. (3) (4) #
    ;;
    "(symbols 'C)"
    (symbols 'C)
    "-> no C:f_1, but .."
    ;;
    "(f_1)"
    (f_1)
    "-> .. call in MAIN by (f_1) works!"

    (lambda () (println "in create-funcs") (context 'C)
     (define (f_1)
      1)
     (context MAIN))
    "(dolist (s (symbols) (= (string s) "f_1")))"
    true
    "-> 'f_1 symbol exists in MAIN, but .."
    "f_1"
    nil
    "-> .. is nil: function not defined yet"
    "(create-funcs)"
    in create-funcs
    MAIN
    "(symbols 'C)"
    ()
    "-> no C:f_1, but .."
    "(f_1)"
    1
    "-> .. call in MAIN by (f_1) works!"
    >

    Some inferred rules regarding context switches:

    [*] (top-level) outside lambdas/maccros:
         
    [*] control symbol creation at compile time;[/list]

  • [*] (lower-level) inside lambdas/macros:
     
    [*] control creation of new symbols at evaluation time;

  • [*] do not affect symbol creation at compile time;

  • [*] do not change symbols already created at compile time.
  • [/list]
    [/list]
    The core point here: only top-level context switches will be honored at compile time, but not lower-level ones.

    An example for a top-level context switch controlled by some evaluation:

    (if flag
        (context 'C_1)
        (context 'C_2))
    (define (create-funcs)
      (define (f) 1))
    (context MAIN)

    The flag controls, in which context symbol f will be created at compile time.

    The following does not work as one might expect:

    (define (switch-context flag)
      (if flag
        (context 'C_1)
        (context 'C_2)))
    (switch-context nil)
    (define (create-funcs)
      (define (f) 1))
    (create-funcs)
    (context MAIN)

    Here f becomes MAIN:f instead of C_2:f (context C_2 will be created, though).



    Leaving a function goes back to the context of the caller; seen by evaluating the following:

    (define (goto-context-inner flag)
      (if flag
        (context 'C_2)
        (context 'C_1))
      (println "inner: " (context)))
    (define (goto-context flag)
      (if flag
        (context 'C_1)
        (context 'C_2))
      (goto-context-inner flag)
      (println "outer: " (context)))
    (goto-context true)
    (println "MAIN: " (context))

    So calling a function for context switches at caller side (e.g. top-level) does not work.

    In other words: context switches in callees do not propagate to callers (which is a good thing).
    #116
    Thanks for the thorough explanation: this really helps!



    Now I'm thinking, that db:create just works, because of the creation of new symbols for contexts at evaluation (and not compile) time.

    In this light I'm feeling more comfortable with my db:create code as before (as I've just stumbled over this issue).



    For me checking the border cases is one of the best ways to learn something (this is not limited to learning of programming languages): especially if someone is knowing these border cases very well and is willing to share his knowledge.



    Thanks,

    Stephan
    #117
    Hello Lutz,



    thanks for your blindingly fast reply!



    I know, why I've switched to MAIN in db:create (because I want to create context symbols in MAIN).

    But I have difficulties to understand, why this is allowed in this case, but makes problems in the other...

    Or in other words: where is the problem in

    ; fails
    (define (create-funcs)
      (println "in create-funcs")
      (context 'C)
      (define (f_1) 1)
      (context MAIN))
    (create-funcs)
    (symbols 'C)  "-> does not work: no sym defined"

    ? I just want to create func (and related) syms in context C here (and switch back thereafter).
    #118
    I think best is to show a use-case.

    There is the following (unfinished, but this does not affect this topic) code:

    (context 'db)

    (define (create (props '("trans" nil)))
      (println props)
      (++ dbCount)
      (context MAIN) ; commenting out this does not work
      (let ((db_sym (sym (append "DB_" (string dbCount))))
            (data_sym (sym (append "Data_" (string dbCount))))
            (meta_sym (sym (append "Meta_" (string dbCount))))
            (trans_sym (sym (append "Trans_" (string dbCount)))))
        (let (db_ctx (new db_proto db_sym)) ; transfer symbols from db_proto: to DB_*:
          (db_ctx:init ; call :init
           (new Tree data_sym)
           (new Tree meta_sym)
           (new Tree trans_sym))
          )))

    (context MAIN)

    Luckily (?) this works as expected.

    If I'm commenting out (context MAIN) I get the error

    ERR: symbol not in MAIN context in function new : DB_1
    called from user defined function db:create

    So is this kind of switching allowed or is there another approach?
    #119
    [Update: -> feature]

    I'm wondering why switching contexts inside a function does not work in the example below.

    Especially because switching inside a func from a non-MAIN context to MAIN already has worked.



    The code:

    ; fails
    (define (create-funcs)
      (println "in create-funcs")
      (context 'C)
      (define (f_1) 1)
      (context MAIN))
    (create-funcs)
    (symbols 'C)  "-> does not work: no sym defined"
    ;;
    ;; works
    (context 'C)
    (define (f_1) 1)
    (context MAIN)
    (symbols 'C) "-> OK: sym defined"

    The session:

    sr@free:~/NewLisp$ newlisp
    newLISP v.10.5.4 64-bit on Linux IPv4/6 UTF-8 libffi, options: newlisp -h

    >
    ;; fails
    (define (create-funcs)
      (println "in create-funcs")
      (context 'C)
      (define (f_1) 1)
      (context MAIN))
    (create-funcs)
    (symbols 'C)  "-> does not work: no sym defined"
    ;;
    ;; works
    (context 'C)
    (define (f_1) 1)
    (context MAIN)
    (symbols 'C) "-> OK: sym defined"

    (lambda () (println "in create-funcs") (context 'C)
     (define (f_1)
      1)
     (context MAIN))
    in create-funcs
    MAIN
    ()
    "-> does not work: no sym defined"
    C
    (lambda () 1)
    MAIN
    (C:f_1)
    "-> OK: sym defined"
    >

    It would be nice to be able to introduce (and later delete) contexts this way (testing is my current use-case triggering this problem).
    #120
    Influenced by a C++ background I've tried to get some assert()-like functionality. Here are the v2.1 results:

    (context 'assert)
    ;;
    (define (assert-expr expr caller-sym no-in-func)
      (++ call_count)
      (if (not (eval expr))
          (throw-error
           (append
            "assert failed for expr: " (string expr)
            (if caller-sym ; implies no-in-func
                (let (caller-fun (eval caller-sym))
                  (if (not (or (lambda? caller-fun) (macro? caller-fun)))
                      (append
                       " (caller " (string caller-sym) " is not a lambda/macro)")
                      (append
                       "n-> " (string no-in-func) ". assert called from "
                       (string caller-sym))))
                ""))))
      true)
    ;;
    (define-macro (assert:assert expr caller-sym no-in-func)
      (assert-expr expr caller-sym no-in-func))
    (set 'pre assert:assert)
    ;;
    (set 'assert_ctx_sym (sym 'assert MAIN))
    (define (assertSyms-cmp ignored e)
      (or
       (= e assert_ctx_sym) ; 'assert would lead to 'assert:assert
       (= e 'assert:assert)
       (= e 'assert:pre)))
    ;;
    ;; inplace mod of a ref-all'ed list referenced by rsym
    (define (sym-mod-ref-all rsym func-sym-mod-ref exp-key func-compare)
      (assert (list? (eval rsym)))
      (local (modFlag)
        (dolist
         (r (if func-compare ;; nil arg is different from no arg here ..
                (ref-all exp-key (eval rsym) func-compare)
                (ref-all exp-key (eval rsym)))) ; .. no nil arg!
         (if (func-sym-mod-ref rsym r exp-key)
             (setq modFlag true)))
        modFlag))
    ;;
    (define (assertCall-sym-mod-ref caller-sym assert-ref exp-key-ignored)
      (local (modFlag)
        (if (= (last assert-ref) 0) ; func call pos?
            (letn ((aref (0 -1 assert-ref)) ; one level up of assert sym
                   (alist (nth aref (eval caller-sym)))) ; nth avoids func call
              (if (<= (length alist) 2) ; (assert) or (assert expr)
                  (begin
                    (if (= (length alist) 1)          ; check for naked assert
                        (push nil alist -1))          ; -> add missing nil expr.
                    (push caller-sym alist -1)        ; add more ..
                    (push (++ no-in-func) alist -1)   ; .. info.
                    (setq modFlag true)
                    ;; replace (args) calls in assert by local-args ..
                    (let (argsRefs (ref-all 'args alist) argsFlag nil)
                      (if (not (null? argsRefs))
                          (begin
                            (dolist
                             (e argsRefs)
                             (if (length (alist (-1 e)) 1) ; just (args)
                                 (begin
                                   (setq argsFlag true)
                                   (setq (alist (0 -1 e)) 'assert:local-args))))
                            ;; .. inited before calling assert.
                            (if argsFlag
                                (setq alist
                                      (list 'begin
                                            '(setq assert:local-args (args))
                                            alist))))))
                    (setq (nth aref (eval caller-sym)) alist)))))
        modFlag))
    ;;
    ;; add missing args of assert calls
    (define (tweak-arguments rsym)
      (if (or (lambda? (eval rsym)) (macro? (eval rsym)))
          (local (no-in-func)
            (if (sym-mod-ref-all rsym assertCall-sym-mod-ref nil assertSyms-cmp)
                (println (string no-in-func) " (assert) call"
                         (if (> no-in-func 1) "s" "") " modified in: " rsym)))))
    ;;
    ;; asserts in contexts
    ;;
    (define (tweak-context ctx)
      (dolist (s (symbols ctx))
              (tweak-arguments s)))
    (define (tweak-contexts contexts)
      (dolist (ctx contexts)
              (tweak-context ctx)))

    (context MAIN)


    Why this is so long is best explained by - a simplified - use-case:

    ;;; [insert assert code from above]
    ;;
    ;; { copy'n'paste
    ;;
    ;; simplified use-case for assert demo purposes
    ;;
    (context 'db_proto)
    ;;
    (define (init dataCtx metaCtx transCtx)
      (setq data dataCtx)
      (setq meta metaCtx)
      (setq trans transCtx)
      (context))
    ;;
    (define (get-data-1 id) ; assert list arg
      (assert:pre (and
                   (string? id)
                   (not (nil? (data id)))))
      (data id))
    ;;
    (define (get-data-2 id) ; assert non-list arg, assert call enumeration
      (assert:pre (string? id))
      (assert (not (nil? (data id))))
      (data id))
    ;;
    (context MAIN)
    ;;
    (context 'db)
    ;;
    (define (create)
      (let (tmp (args))
        (assert (null? tmp)))
      (++ dbCount)
      (context MAIN)
      (let ((db_sym (sym (append "DB_" (string dbCount))))
            (data_sym (sym (append "Data_" (string dbCount)))))
        (let (db_ctx (new db_proto db_sym)) ; transfer syms from db_proto: to DB_*
          (db_ctx:init ; call :init
           (new Tree data_sym)))))
    (define (create_alt)
      (assert (null? (args)))
      (++ dbCount)
      (context MAIN)
      (let ((db_sym (sym (append "DB_" (string dbCount))))
            (data_sym (sym (append "Data_" (string dbCount)))))
        (let (db_ctx (new db_proto db_sym)) ; transfer syms from db_proto: to DB_*
          (db_ctx:init ; call :init
           (new Tree data_sym)))))
    ;;
    (context MAIN)
    ;;
    ;; } copy'n'paste

    ;; { copy'n'paste
    ;;
    ;; 1. test without assert call modifications
    ;; init
    (set 'dbc (db:create))
    (dbc:data "foo" "bar") ; put something into data
    ;;
    ;; } copy'n'paste
    ;;

    ;; try manually from here
    ;; a.
    (db:create "forbidden arg")
    ;; -> this gives db:create info *without* tweaking asserts
    (db:create_alt "forbidden arg")
    ;; -> this does not work without tweaking (should throw)
    ;; b.
    (dbc:get-data-1) ; missing id
    (dbc:get-data-2) ; missing id
    ;; c.
    (dbc:get-data-1 "wrong id")
    (dbc:get-data-2 "wrong id")
    ;; d.
    (dbc:get-data-1 "foo") ; correct id
    (dbc:get-data-2 "foo") ; correct id
    ;;

    ;; { copy'n'paste
    ;;
    ;; 2. test with assert call modifications
    ;; modify assert calls
    (assert:tweak-contexts '(db db_proto))
    ;; short form for
    ;;   (assert:tweak-context db) (assert:tweak-context db_proto)
    ;;
    ;; (assert:tweak-context db_proto)
    ;;   short from for:
    ;;     (assert:tweak-arguments 'db_proto:get-data-1)
    ;;     (assert:tweak-arguments 'db_proto:get-data-2)
    ;;
    ;; just one func:
    ;;   (assert:tweak-arguments 'db:create)
    ;;
    ;;
    ;; init of new db (old one contains unmodified calls)
    (set 'dbc (db:create))
    (dbc:data "foo" "bar") ; put something into data
    ;;
    ;; } copy'n'paste
    ;;
    ;; try manually from here
    ;; a.
    (db:create "forbidden arg")
    ;; -> db:create info redundant after modifying asserts
    (db:create_alt "forbidden arg")
    ;; -> this does work now after tweaking (throws)
    ;; b.
    (dbc:get-data-1) ; missing id
    (dbc:get-data-2) ; missing id
    ;; c.
    (dbc:get-data-1 "wrong id")
    (dbc:get-data-2 "wrong id")
    ;; d.
    (dbc:get-data-1 "foo") ; correct id
    (dbc:get-data-2 "foo") ; correct id
    ;;


    Case a. (db:create "forbidden arg") is 'interesting', which creates redundant caller info after tweaking asserts by (assert:tweak-contexts '(db db_proto)).

    In the other cases added caller info would be missing without tweaking asserts: it won't be shown as part of output of throw callstacks (this has been a reason to start with these tweaks).



    To avoid calling (args) in a wrong function context, a temp var will be inserted in this case; see (db:create) after tweaking its assert.



    Any suggestions for improvements are welcome (I like to learn).



    Possible extensions are tweaks for:

    [*] switching assert checks on/off (selectively: assert:pre macros could be on, others off)
  • [*] code coverage statistics
  • [/list]


    Versions:

       
    [*]v2.1.1: [fix] use-case code
     
  • [*]v2.1: [enh] context-wide tweak of asserts

  •  
  • [*]v2:
     

         
    [*][fix] simplified interface: (and)'ed expressions easily replace - unneeded - quoted list args (without lack of functionality)

  •    
  • [*][enh] (args) call in assert expr gives caller args now (by introducing var for storing its local value)

  •   [/list]

     
  • [*]v1: first shot
  • [/list]