Author Topic: LispFunction SubList  (Read 6689 times)

0 Members and 1 Guest are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
LispFunction SubList
« on: October 04, 2010, 04:42:57 AM »

a little fugly ... anyone want to try making it pretty and faster.
Competes well with Lisp for small returns from large lists.


Refer  : http://www.theswamp.org/index.php?topic=35125.0
-={ Challenge }=- SubStr for Lists
(SubList <list> <start> [<length>])
start is zero based
if length is nil, return remainder of list


Code: [Select]
(setq TestList '(1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
                 16  17  18  19  20  21  22  23  24  25  26  27  28  29  30
                )
      StartIndex 9
      ReturnCount 13
)
(NetSubList TestList StartIndex ReturnCount)
;;-> (10 11 12 13 14 15 16 17 18 19 20 21 22)
Code: [Select]
// CodeHimBelonga kdub 2010.10.04
//
using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;

using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(kdub_Testing.MyCommands))]

namespace kdub_Testing
{
    public class MyCommands
    {
        [LispFunction("NetSubList")]
        public ResultBuffer MyLispFunction_SubList
            (ResultBuffer rbArgs)
        {
            ResultBuffer rb1 = new ResultBuffer();

            TypedValue[] tvArray = rbArgs.AsArray();
            int arraylen = tvArray.Length;
            int listCount = arraylen - 4;

            TypedValue rCount = tvArray[arraylen - 1];

            int StartIndex = (short)tvArray[arraylen - 2].Value;

            int ItemsToReturn = (short)rCount.TypeCode == (short)LispDataType.Nil
                                ? (int)listCount
                                : (short)rCount.Value;

            int StopIndex = listCount < StartIndex + ItemsToReturn
                            ? listCount + 1
                            : StartIndex + ItemsToReturn + 1;
             
            rb1.Add(new TypedValue((int)LispDataType.ListBegin, null));           
             
            for (int i = StartIndex + 1 ; i < StopIndex ; i++)
            {
                rb1.Add(tvArray[i]);               
            }
            rb1.Add(new TypedValue((int)LispDataType.ListEnd, null));

            return rb1;
        }
    }
}
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8718
  • AKA Daniel
Re: LispFunction SubList
« Reply #1 on: October 04, 2010, 05:16:53 AM »
It's a lot harder in C++/C# as you really should identify the list within the Resultbuffer rbArgs,  Then test for the second and third arguments,
otherwise issuing TestList '( 1 2 3 4 5 6 7 8 9)  would try to use  '9' and the ')' as the index and count. right?

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: LispFunction SubList
« Reply #2 on: October 04, 2010, 05:22:27 AM »
There IS an expectation that the correct quantum of parameters will be used :-D
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8718
  • AKA Daniel
Re: LispFunction SubList
« Reply #3 on: October 04, 2010, 05:24:50 AM »
You would think  :laugh:

kaefer

  • Guest
Re: LispFunction SubList
« Reply #4 on: October 04, 2010, 08:02:48 PM »
You would think  :laugh:

Dunno about thinking, the fun part will be just trying to make that bullet-proof.

What needs to be special-cased? The input buffer can be empty or may have any number of arguments; the input list can be NIL or may be a list with 2 or 3 numeric elements; numeric parameters can have 16 or 32 bits, anything else? And what is an appropriate error-handling, apart from returning NIL at every corner?

I would start by rewriting the spec, with start and stop (instead of length) position first and the input list at the end of the arguments.

So here's my take, with heavy use of F# patterns:
Code: [Select]
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime

let rb a = new ResultBuffer(a)
let (|RB|) (rb: ResultBuffer) = rb.AsArray() |> List.ofArray

let tv v = new TypedValue(int v)
let (|TV|_|) k (tv: TypedValue) =
    if tv.TypeCode = int16 k then Some tv.Value else None

let nil = rb [| tv LispDataType.Nil |]
let lispList listData =
    [ [| tv LispDataType.ListBegin |]; listData; [| tv LispDataType.ListEnd |] ]
    |> Array.concat |> rb

let (|Int16|) = unbox<int16> >> int
let (|Int32|) = unbox<int32> >> int
let (|Array|) = Array.ofList >> fun a -> a.[0 .. a.Length - 2]
let tvf f = new TypedValue(int LispDataType.Double, f)
let (|Point2d|) o = let p = unbox<Point2d> o in [| tvf p.X; tvf p.Y |]
let (|Point3d|) o = let p = unbox<Point3d> o in [| tvf p.X; tvf p.Y; tvf p.Z |]

[<LispFunction "MyNetSubList">]
let MyLispFunction_SubList = function
    | null -> nil
    | RB (TV LispDataType.Int16 (Int16 start) :: xs)
    | RB (TV LispDataType.Int32 (Int32 start) :: xs) ->
        match xs with
        | TV LispDataType.Int16 (Int16 stop) :: ys
        | TV LispDataType.Int32 (Int32 stop) :: ys ->
            match ys with
            | TV LispDataType.Point2d (Point2d zs) :: _
            | TV LispDataType.Point3d (Point3d zs) :: _
            | TV LispDataType.ListBegin _ :: (Array zs) ->
                let res = zs.[max 0 start .. min stop (zs.Length - 1)]
                if Array.isEmpty res then nil else lispList res
            | _ -> nil
        | _ -> nil
    | _ -> nil
Regards, Thorsten




Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: LispFunction SubList
« Reply #5 on: October 04, 2010, 08:34:58 PM »

Quote
I would start by rewriting the spec, with start and stop (instead of length) position first and the input list at the end of the arguments.

:-D

I wanted to do that, but considered that the 'client' had defined the spec'
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8718
  • AKA Daniel
Re: LispFunction SubList
« Reply #6 on: October 04, 2010, 10:23:36 PM »
Kerry's right, the client wrote the spec ! Plus to do right, the optional argument need not be used as in substr  :evil:

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: LispFunction SubList
« Reply #7 on: October 04, 2010, 11:15:42 PM »
Kerry's right, the client wrote the spec ! Plus to do right, the optional argument need not be used as in substr  :evil:

now you're getting picky.

... but I'll argue with you.
The function needs to be able to replace any of the lisp routines written for the challenge { though some of them don't meet the spec either    :angel:}
The routine I wrote still has some issues,
pass nil as start 2nd param
pass negative number as 2nd or 2rd param
pass anything other than integer as 2nd or 2rd param
pass anything other than a list as the 1st parameter

Other than that and the fact it looks ugly and is slower than most of the alternatives it's pretty good   :-P
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Glenn R

  • Guest
Re: LispFunction SubList
« Reply #8 on: October 05, 2010, 06:54:30 AM »
Code: [Select]
[LispFunction("NetSubList")]
public ResultBuffer netSubListLispFunction(ResultBuffer args) // This method can have any name
{
    ResultBuffer rb1 = new ResultBuffer();

    TypedValue[] tvArray = args.AsArray();
    int arraylen = tvArray.Length;
    int listCount = arraylen - 4;

    TypedValue rCount = tvArray[arraylen - 1];

    int StartIndex = (short)tvArray[arraylen - 2].Value;

    int ItemsToReturn = (short)rCount.TypeCode == (short)LispDataType.Nil
                        ? (int)listCount
                        : (short)rCount.Value;

    ArraySegment<TypedValue> seg = new ArraySegment<TypedValue>(tvArray, StartIndex + 1, ItemsToReturn);

    rb1.Add(new TypedValue((int)LispDataType.ListBegin, null));
    for (int i = seg.Offset; i < (seg.Offset + seg.Count); i++)
    {
        rb1.Add(seg.Array[i]);
    }
    rb1.Add(new TypedValue((int)LispDataType.ListEnd, null));

    return rb1;

}

Glenn R

  • Guest
Re: LispFunction SubList
« Reply #9 on: October 05, 2010, 07:14:25 AM »
...with some LINQage...

Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;

[assembly: CommandClass(typeof(KwbLispList.MyCommands))]

namespace KwbLispList
{
    public class MyCommands
    {
        [LispFunction("NetSubList")]
        public ResultBuffer netSubListLispFunction(ResultBuffer args) // This method can have any name
        {
            ResultBuffer rb1 = new ResultBuffer();

            TypedValue[] tvArray = args.AsArray();
            int arraylen = tvArray.Length;
            int listCount = arraylen - 4;

            TypedValue rCount = tvArray[arraylen - 1];

            int StartIndex = (short)tvArray[arraylen - 2].Value;

            int ItemsToReturn = (short)rCount.TypeCode == (short)LispDataType.Nil
                                ? (int)listCount
                                : (short)rCount.Value;

            rb1.Add(new TypedValue((int)LispDataType.ListBegin, null));
            tvArray.Skip(++StartIndex).Take(ItemsToReturn).ForEach(rb1.Add);
            rb1.Add(new TypedValue((int)LispDataType.ListEnd, null));

            return rb1;

        }

    }

    public static class Foo
    {
        public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
        {
            foreach (T item in enumeration)
            {
                action(item);
            }
        }
    }

}

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8718
  • AKA Daniel
Re: LispFunction SubList
« Reply #10 on: October 05, 2010, 09:08:59 AM »
mine

Code: [Select]
  //(SubList '(0 1 2 3 4 5) 2 3)
    [LispFunction("sublist")]
    static public ResultBuffer sublist(ResultBuffer InBuff)
    {
      try
      {
        Int32 listBegin = 0;
        Int32 listEnd = 0;
        Int32 startPos = 0;
        Int32 length = 0;

        if (InBuff == null)
          return null;

        List<TypedValue> InList = new List<TypedValue>(InBuff.AsArray());

        for (Int32 idx = 0; idx < InList.Count; idx++)
        {
          switch ((LispDataType)InList[idx].TypeCode)
          {
            case LispDataType.ListBegin: listBegin = idx+1; break;
            case LispDataType.ListEnd: listEnd = idx; break;
          }
        }

        startPos = Convert.ToInt32(InList[listEnd + 1].Value);

        if (InList.Count > (listEnd + 2))
          length = Convert.ToInt32(InList[listEnd + 2].Value);
        else
          length = listEnd-startPos-1;

        if (listEnd - startPos <= length)
          throw new System.Exception("out of range");

        return new ResultBuffer(InList.GetRange(startPos+1, length).ToArray());
      }
      catch (System.Exception ex)
      {
        return null;
      }
    }
« Last Edit: October 05, 2010, 09:24:30 AM by __declspec »

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8718
  • AKA Daniel
Re: LispFunction SubList
« Reply #11 on: October 05, 2010, 09:31:17 AM »
still not dan proof (SubList '((0 1 2 3 4 5)) 0 3) outputs junk

(SubList '(0 1 2 3 4 5) "1" "3") works though   :laugh:

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: LispFunction SubList
« Reply #12 on: October 05, 2010, 09:58:39 AM »
Here're my 2 cents:

Quote
_$ (gc-sublist '(0 1 2 3 4 5 6 7 8 9) 4 3)
(4 5 6)
_$ (gc-sublist '(0 1 2 3 4 5 6 7 8 9) 4 nil)
(4 5 6 7 8 9)
_$ (gc-sublist '(0 1 2 3 4 5 6 7 8 9) 4)
(4 5 6 7 8 9)
_$ (gc-sublist '(0 1 2 3 4 5 6 7 8 9) 4 32)
(4 5 6 7 8 9)

Code: [Select]
[LispFunction("gc-SubList")]
        public ResultBuffer MyLispFunction(ResultBuffer args)
        {
            if (args == null)
                return null;
            try
            {
                List<TypedValue> tValues = new List<TypedValue>(args.AsArray());
                int lstEnd = tValues.LastIndexOf(new TypedValue(5017, -1));
                int start = Convert.ToInt32(tValues[lstEnd + 1].Value) + 1;
                int length = lstEnd - start;
                if(tValues.Count == lstEnd + 3 && tValues[lstEnd + 2].TypeCode != (short)LispDataType.Nil)
                    length = Math.Min(Convert.ToInt32(tValues[lstEnd + 2].Value), length);
                return new ResultBuffer(tValues.GetRange(start, length).ToArray());
            }
            catch(System.Exception ex)
            {
                Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.Message);
                return null;
            }
        }
Speaking English as a French Frog

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8718
  • AKA Daniel
Re: LispFunction SubList
« Reply #13 on: October 05, 2010, 10:28:59 AM »

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: LispFunction SubList
« Reply #14 on: October 05, 2010, 10:49:47 AM »
Thanks Daniel.

Here's a safer one (only allows LISP INT type (short or int) as second and [optional] third arguments)

Code: [Select]
[LispFunction("gc-SubList")]
        public ResultBuffer MyLispFunction(ResultBuffer args)
        {
            if (args == null)
                return null;
            try
            {
                List<TypedValue> tValues = new List<TypedValue>(args.AsArray());
                int lstEnd = tValues.LastIndexOf(new TypedValue(5017, -1));
                int start = 1;
                switch (tValues[lstEnd + 1].TypeCode)
                {
                    case (short)LispDataType.Int16:
                        start += (short)tValues[lstEnd + 1].Value;
                        break;
                    case (short)LispDataType.Int32:
                        start += (int)tValues[lstEnd + 1].Value;
                        break;
                    default:
                        return null;
                }
                int length = lstEnd - start;
                if (tValues.Count == lstEnd + 3 && tValues[lstEnd + 2].TypeCode != (short)LispDataType.Nil)
                    switch (tValues[lstEnd + 2].TypeCode)
                    {
                        case (short)LispDataType.Int16:
                            length = Math.Min((short)tValues[lstEnd + 2].Value, length);
                            break;
                        case (short)LispDataType.Int32:
                            length = Math.Min((int)tValues[lstEnd + 2].Value, length);
                            break;
                        default:
                            return null;
                    }
                return new ResultBuffer(tValues.GetRange(start, length).ToArray());
            }
            catch
            {
                return null;
            }
        }
Speaking English as a French Frog

JohnK

  • Administrator
  • Seagull
  • Posts: 10648
Re: LispFunction SubList
« Reply #15 on: October 05, 2010, 11:13:34 AM »
Good idea; i just created a generic List class in C++. Very good practice for me. Thanx guys.
TheSwamp.org (serving the CAD community since 2003)
Member location map - Add yourself

Donate to TheSwamp.org

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8718
  • AKA Daniel
Re: LispFunction SubList
« Reply #16 on: October 05, 2010, 01:42:22 PM »
here is the resbuf version   :-o

Code: [Select]
tatic resbuf* cloneNode( const resbuf* pRb )
  {
    if (pRb == NULL)
      return NULL;

    struct resbuf* pRbTmp = NULL;

    if(pRb->restype > 5000)
    {
      if(pRb->restype == RTSTR)
      {
        pRbTmp = acutNewRb(pRb->restype);
        if(pRbTmp)
          pRbTmp->resval.rstring = _tcsdup(pRb->resval.rstring);
        return pRbTmp;
      }
      else
      {
        pRbTmp = acutNewRb(pRb->restype);
        if(pRbTmp)
          memcpy(&pRbTmp->resval,&pRb->resval, sizeof(pRbTmp->resval));
        return pRbTmp;
      }
    }
    return pRbTmp;
  }



  //(SubList '(0 1 2 3 4 5) 2 3)
  static int ads_sublist(void)
  {
    struct resbuf *pRbOutHead = NULL;
    struct resbuf *pRbOutTail = NULL;
    struct resbuf *pRbInHead = acedGetArgs();

    INT_PTR rtlb = 0;
    INT_PTR rtle = 0;
    INT_PTR startPos = 0;
    INT_PTR length = 0;
    INT_PTR listLength = 0;
    INT_PTR inIdx = 0;
    INT_PTR outIdx = 0;

    for(const struct resbuf *piter = pRbInHead; piter!=NULL; piter=piter->rbnext)
    {
      if(piter->restype==RTLB)
      {
        rtlb = inIdx;
      }
      else if (piter->restype==RTLE)
      {
        rtle = inIdx;
        if(piter->rbnext)
        {
          if(piter->rbnext->restype == RTSHORT ||
             piter->rbnext->restype == RTLONG)
          {
            startPos = piter->rbnext->resval.rlong;
          }
        }
        if(piter->rbnext->rbnext)
        {
          if(piter->rbnext->rbnext->restype == RTSHORT ||
             piter->rbnext->rbnext->restype == RTLONG)
          {
            length = piter->rbnext->rbnext->resval.rlong;
          }
        }
      }
      inIdx++;
    }

    if(inIdx<4)
      return (RSERR);

    if(length == 0 || length > (rtle-1 - startPos) || length > (rtle-1+rtlb+1))
      length = rtle-1 - startPos;

    for(const struct resbuf *piter = pRbInHead; piter!=NULL; piter=piter->rbnext)
    {
      if(outIdx == startPos+1)
        pRbOutTail= pRbOutHead = cloneNode(piter);
      if(outIdx > startPos+1 && length + startPos+1 > outIdx && outIdx < rtle)
        pRbOutTail = pRbOutTail->rbnext = cloneNode(piter);
      outIdx++;
    }
    acedRetList(pRbOutHead);
    acutRelRb(pRbOutHead);
    return (RSRSLT) ;
  }

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8718
  • AKA Daniel
Re: LispFunction SubList
« Reply #17 on: October 06, 2010, 03:55:40 AM »
This one uses the same memory received from lisp

Code: [Select]
//(SubList '(0 1 2 3 4 5) 2 3)
  static int ads_sublist(void)
  {
    struct resbuf *pRbTmp = NULL;
    struct resbuf *pRbOutHead = NULL;
    struct resbuf *pRbOutTail = NULL;
    struct resbuf *pRbInHead = acedGetArgs();

    INT_PTR rtlb = 0;
    INT_PTR rtle = 0;
    INT_PTR startPos = 0;
    INT_PTR length = 0;
    INT_PTR listLength = 0;
    INT_PTR inIdx = 0;
    INT_PTR outIdx = 0;

    for(const struct resbuf *piter = pRbInHead; piter!=NULL; piter=piter->rbnext)
    {
      if(piter->restype==RTLB)
      {
        rtlb = inIdx;
      }
      else if (piter->restype==RTLE)
      {
        rtle = inIdx;
        if(piter->rbnext)
        {
          if(piter->rbnext->restype == RTSHORT ||
             piter->rbnext->restype == RTLONG)
          {
            startPos = piter->rbnext->resval.rlong;
          }
        }
        if(piter->rbnext->rbnext)
        {
          if(piter->rbnext->rbnext->restype == RTSHORT ||
             piter->rbnext->rbnext->restype == RTLONG)
          {
            length = piter->rbnext->rbnext->resval.rlong;
          }
        }
      }
      inIdx++;
    }

    if(inIdx<4)
      return (RSERR);

    if(length == 0 || length > (rtle-1 - startPos) || length > (rtle-1+rtlb+1))
      length = rtle-1 - startPos;

    for(struct resbuf *piter = pRbInHead; piter!=NULL; piter=piter->rbnext)
    {
      if(outIdx == startPos+1)
        pRbOutTail= pRbOutHead = piter;
      if(outIdx > startPos+1 && length + startPos+1 > outIdx && outIdx < rtle)
        pRbOutTail = pRbOutTail->rbnext = piter;
      outIdx++;
    }

    if(pRbOutTail->rbnext)
    {
      pRbTmp = pRbOutTail->rbnext;
      pRbOutTail->rbnext = NULL;
    }
    acedRetList(pRbOutHead);
    pRbOutTail->rbnext =pRbTmp;
    return (RSRSLT) ;
  }