• Skip to main content
  • Skip to search
  • Skip to footer
Cadence Home
  • This search text may be transcribed, used, stored, or accessed by our third-party service providers per our Cookie Policy and Privacy Policy.

  1. Community Forums
  2. Custom IC SKILL
  3. Destructive form of "cons" - efficiently prepending an item...

Stats

  • Locked Locked
  • Replies 4
  • Subscribers 144
  • Views 1252
  • Members are here 0
This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Destructive form of "cons" - efficiently prepending an item to a procedure's argument which is a list

JustinSilver
JustinSilver 9 months ago

Hello,

I was looking to destructively and efficiently modify a list that was passed in as an argument to a procedure, by prepending an item to the list.

I noticed that cons lets you do this efficiently, but the operation is non-destructive. Hence this wouldn't work if you are trying to modify a function's list parameter in place.

Here is an example of trying to add "0" to the front of a list:

procedure( attempt_to_prepend_list(l elem)
    l = cons(elem l)
)
a = list(1 2 3)
==> (1 2 3)
attempt_to_prepend_list(a 0)
==> (0 1 2 3)
a
==> (1 2 3)
As we can see, the original list is not prepended.
Here is a function though which achieves the desired result while being efficient. Namely, the following function does not create any new lists and only uses fast methods like cons, rplacd, and rplaca
procedure( prepend_list(l elem)
    ; cons(car(l) cdr(l)) results in a new list with the car(l) duplicated
    ; we then replace the cdr of l so that we are now pointing to this new list
    rplacd(l cons(car(l) cdr(l)))

    ; we replace the previously duplicated car(l) with the element we want
    rplaca(l elem)
)
a = list(1 2 3)
==> (1 2 3)
prepend_list(a 0)
==> (0 1 2 3)
a
==> (0 1 2 3)
This works for me, but I find it surprising there is no built-in function to do this. Am I perhaps overlooking something in the documentation? I know that tconc is an efficient and destructive way to append items to the end of a list, but there isn't an equivalent for the front of the list?

  • Cancel
  • henker
    henker 9 months ago

    I don't think this can work with a procedure, you need something that does not evaluate the arguments, e.g. defmacro.

    You may have a look at the article "How to pass a variable by name to a SKILL function?"

    https://support.cadence.com/apex/ArticleAttachmentPortal?id=a1Od0000000naCvEAI&pageName=ArticleContent

    Another option could be to wrap the list or whatever other variable in a disembodied property list and pass that to the function, e.g.

    procedure( attempt_to_prepend_list(arg elem)
    	arg->a = cons(elem arg->a)
    )
    
    arg = ncons(nil)
    arg->a = list(1 2 3)
    arg
    ==> (nil a (1 2 3))
    
    attempt_to_prepend_list(arg 5)
    
    arg
    ==> (nil a (5 1 2 3))

    Or also pass the name of the property:

    procedure( attempt_to_prepend_list(arg name elem)
    	putprop(arg cons(elem get(arg name)) name)
    )
    
    attempt_to_prepend_list(arg 'a 5)

    Passing a table to the function would also work and might be faster with many entries than the list.

    Regards,

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett 9 months ago in reply to henker

    I didn't see the original post (for some reason, I didn't get any email notification; I've asked our IT to investigate why as this has happened a couple of times recently).

    Without having read the original post, I came up with a solution the same as JustinSilver 's proposed solution. The fundamental issue is that variables in SKILL are pointers - and if a variable is pointing at a list, it's pointing at the list cell that is the car of the list. That means that you can change the contents of that list cell (rplaca) or the next item in the list (rplacd) whilst the pointer keeps pointing at the same list cell. If you need to add a new value to the beginning of the list, the only way to do that is to update the pointer that the variable holds, which can't be done. Macros could achieve this, but only for the single variable you are updating - in essence you would generate the expression to reassign the variable to the list with the newly consed value on the front. 

    You could also always pass around a list with an additional dummy value on the front, and insert the new value between the first value and the rest of the list (similar to henker 's DPL-based solution, without the DPL.

    The reason why there's not a built-in function to do this is:

    1. Your workaround code won't work universally - for example, it relies on the list being non-nil (there is no way to make it work with an empty list; this is the key reason why disembodied property lists have a dummy value, typically nil, at the beginning - as that way the list can always be destructively modified).
    2. There's no efficiency benefit as there is with tconc; in this case it's actually less efficient than using cons (cons is very efficient as it is). In general, destructive list functions are few and far between, and only used when there's a clear performance benefit in doing so (or to implement some special structure such as a disembodied list)

    In a functional language, it would be more usual to return the updated list rather than relying on a side-effect of changing the list in place; if you really want that I'd go for the dummy value at the beginning approach that I mentioned earlier because that would then work if the real list contents are nil or any number of elements (similar to how DPLs work).

    Andrew

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • JustinSilver
    JustinSilver 9 months ago in reply to Andrew Beckett

    All great points. Thanks for the very thorough explanation.

    My particular use case for this was to parse a cellview and iteratively pre-append to a list based on the number of instances, nets, etc. Initially I thought I would need to wrap these lists in another list (since I can only return a single variable) and I wanted to avoid that. However, it looks like DPL can do this cleanly.

    In the end though, I ended up using the tconc approach (passing tconc structures as parameters) since the choice of appending vs. pre-appending wasn't such a big deal for me.

    -Justin

    • Cancel
    • Vote Up 0 Vote Down
    • Cancel
  • Andrew Beckett
    Andrew Beckett 9 months ago in reply to JustinSilver

    Justin,

    Note you can always have your function return multiple things as a list:

    list(retVal1 retVal2 retVal3)

    and then in the calling function do:

    destructuringBind((myVar1 myVar2 myVar3) yourFunctionWithThreeRetValues()
      ; do something with myVar1 myVar2 myVar3
    )

    Andrew

    • Cancel
    • Vote Up +1 Vote Down
    • Cancel

Community Guidelines

The Cadence Design Communities support Cadence users and technologists interacting to exchange ideas, news, technical information, and best practices to solve problems and get the most from Cadence technology. The community is open to everyone, and to provide the most value, we require participants to follow our Community Guidelines that facilitate a quality exchange of ideas and information. By accessing, contributing, using or downloading any materials from the site, you agree to be bound by the full Community Guidelines.

© 2025 Cadence Design Systems, Inc. All Rights Reserved.

  • Terms of Use
  • Privacy
  • Cookie Policy
  • US Trademarks
  • Do Not Sell or Share My Personal Information