Author Topic: Defining a LISP function  (Read 13445 times)

0 Members and 1 Guest are viewing this topic.

Grrr1337

  • Swamp Rat
  • Posts: 812
Defining a LISP function
« on: October 04, 2018, 08:05:14 AM »
Hey .NET developers,
I wanted to ask you if its possible to define a LISP function via C#/.NET , by providing the actual LISP code ?
I mean having the LISP code inside of the .NET code and writing it+defining it on-the-fly.

So basically like passing this into VLIDE's console and defining it:

Code - Auto/Visual Lisp: [Select]
  1. (defun C:test ( / str )
  2.   (if (setq str (getstring "\nType something: "))
  3.     (alert str)
  4.   )
  5.   (princ)
  6. )

Second question: how can you call the LISP function itself, again via .NET ?
« Last Edit: October 04, 2018, 08:08:35 AM by Grrr1337 »
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #1 on: October 04, 2018, 08:40:52 AM »
Hi,

You can simply do it this way: entering the code in the command line with Document.SendStringToExecute() method and call the LISP function with Document.SendStringToExecute() method (which does not run synchronously or with Application.Invoke() method (which runs synchronously and allows to get the LISP function return value.

Define the LISP:
Code - C#: [Select]
  1.         [CommandMethod("LISPDEF")]
  2.         public static void LispDefine()
  3.         {
  4.             var code = "(defun C:test ( / str ) " +
  5.                 "(if (setq str(getstring \"\nType something: \"))" +
  6.                 "(alert str)" +
  7.                 ")" +
  8.                 "(princ)" +
  9.                 ")";
  10.             var doc = Application.DocumentManager.MdiActiveDocument;
  11.             doc.SendStringToExecute(code + "\n", false, false, false);
  12.         }

Call the LISP:
Code - C#: [Select]
  1.         [CommandMethod("LISPCALL")]
  2.         public static void LispCall()
  3.         {
  4.             var args = new ResultBuffer(new TypedValue(1, "c:test"));
  5.             Application.Invoke(args);
  6.         }

But, IMO, you should avoid to mix LISP from .NET this way. Most of the this brings more problems than solutions.
Speaking English as a French Frog

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #2 on: October 04, 2018, 10:11:13 AM »
Hi gile, (I was secretly waiting for you to answer)

Define the LISP:
Code - C#: [Select]
  1. { code }

Call the LISP:
Code - C#: [Select]
  1. { code }

Thanks alot!
The reason of my question is because at the moment I'm not enough confident/comfortable to throw myself fully with .NET
I have learned a bit on how to display a windows form and gather inputs, (you know: Form > DCL)..

However after the inputs are collected, starts the scary part of the code - performing operations in ACAD (which I find easier to do with LISP for now).
So this should be the part with more coding, which means being thrown with many different exceptions, without idea on how to debbug them (because of the uncomfortable property I got).


But I agree with you on this -
But, IMO, you should avoid to mix LISP from .NET this way. Most of the this brings more problems than solutions.

My plan is slowly to delve into .NET, by reducing the gaps that I fill via LISP atm.
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: Defining a LISP function
« Reply #3 on: October 04, 2018, 11:13:44 AM »
LispFunction attribute might be of some interest, allows for sending / returning lists and other goodies 

Code: [Select]
         [LispFunction("lisp01")]
        // Note: you can return types bool, int, double, Point2d, Point3d, ObjectID, TypedValue,
        // ResultBuffer or null to lisp
        static public ResultBuffer lisp01(ResultBuffer pArgs)
        {
            Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
            try
            {
                if (pArgs != null)
                {
                    List<TypedValue> inArray = new List<TypedValue>(pArgs.AsArray());
                    return new ResultBuffer(inArray.ToArray());
                }
            }
            catch (System.Exception ex)
            {
                _AcAp.Application.ShowAlertDialog(
                    string.Format("\nError: {0}\nStackTrace: {1}", ex.Message, ex.StackTrace));
            }
            return null;
        }

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #4 on: October 04, 2018, 11:18:59 AM »
If the issue is about dialog boxes, you can build the dialog box with .NET and expose it to LISP via a LispFunction (i.e. a method which is decored with the LispFunction attribute). If I had to mix .NET and LISP I'd rather do it this way

Here's a trivial example with a simple dialog box containing a TextBox and a NumericUpDown:

The Dialog class exposes the data as properties:
Code - C#: [Select]
  1. using System.Windows.Forms;
  2.  
  3. namespace LispDialogSample
  4. {
  5.     public partial class Dialog : Form
  6.     {
  7.         public Dialog()
  8.         {
  9.             InitializeComponent();
  10.  
  11.             // the following properties can be set in the [Design] tab
  12.             btnOk.DialogResult = DialogResult.OK;
  13.             btnCancel.DialogResult = DialogResult.Cancel;
  14.         }
  15.  
  16.         public string TextValue => textBox1.Text;
  17.  
  18.         public int NumericValue => (int)numericUpDown1.Value;
  19.     }
  20. }

The LispFunctions class defines the 'GetDataFromDialog' LISP function which open the dialog to get the user inputs and returns the data as a list (or nil if Cancel):
Code - C#: [Select]
  1. using System.Windows.Forms;
  2.  
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Runtime;
  5.  
  6. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  7.  
  8. namespace LispDialogSample
  9. {
  10.     public class LispFunctions
  11.     {
  12.         [LispFunction("GetDataFromDialog")]
  13.         public static ResultBuffer GetDataFromDialog(ResultBuffer resbuf)
  14.         {
  15.             var dialog = new Dialog();
  16.             var result = AcAp.ShowModalDialog(dialog);
  17.             if (result != DialogResult.OK)
  18.                 return null;
  19.             return new ResultBuffer(
  20.                 new TypedValue((int)LispDataType.Text, dialog.TextValue),
  21.                 new TypedValue((int)LispDataType.Int32, dialog.NumericValue));
  22.         }
  23.     }
  24. }
  25.  

While netloaded, you can use this function in your LISP routines:
Code - Auto/Visual Lisp: [Select]
  1. (if (setq data (getdatafromdialog))
  2.   (alert (strcat "Text: " (car data) "\nNumber: " (itoa (cadr data))))
  3.   (alert "Cancelled operation")
  4. )

EDIT: Daniel was faster...
Speaking English as a French Frog

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #5 on: October 04, 2018, 05:09:28 PM »
LispFunction attribute might be of some interest, allows for sending / returning lists and other goodies 

Code: [Select]
[LispFunction("lisp01")]
...

Thanks nullptr, it looks quite interesting -

Code - Auto/Visual Lisp: [Select]
  1. _$ lisp01
  2. #<SUBR @000000f9c7658908 <EXRXSUBR>>
  3. _$ (lisp01 5)
  4. (5)
  5. _$ (lisp01 1 2 3 4)
  6. (1 2 3 4)
  7. _$ (lisp01)
  8. nil
  9. _$ (lisp01 '("A" "B" "C" "D"))
  10. ("A" "B" "C" "D")
  11. _$ (lisp01 '((00 01 02 03)(10 11 12 13)(20 21 22 23)(30 31 32 33)))
  12. ((0 1 2 3) (10 11.0 12.0 13.0) (20 21 22 23) (30 31 32 33))
  13. _$ (lisp01 1 2 3 '(1 2 3) '(4 5 6))
  14. (1 2 3 (1.0 2.0 3.0) (4.0 5.0 6.0))
  15.  

Although, I'm not sure about its purpose. Also seems to throw exceptions when symbols, or list of symbols are passed: (lisp01 'abc) or (lisp01 '(a b c))
BTW it reminds me of the list LISP function, which provides similar returns.


If the issue is about dialog boxes, you can build the dialog box with .NET and expose it to LISP via a LispFunction (i.e. a method which is decored with the LispFunction attribute). If I had to mix .NET and LISP I'd rather do it this way

Indeed thats the issue!
Now I know a bit more (about the LispFunction attribute)..
Indeed I now prefer your way of mixing .NET and LISP.. still I'm happy that you revealed the alternative approach per my original question!


Here's a trivial example with a simple dialog box containing a TextBox and a NumericUpDown:

Thanks for this example, gile - Highly appreciated!
Although I got some errors on declaring the public variables from the dialog class -

Code - C#: [Select]
  1. public string TextValue => textBox1.Text;
  2. public int NumericValue => (int)numericUpDown1.Value;

Code: [Select]
Error 9 'LispDialogSample_Gile.Dialog.numericUpDown1' is a 'field' but is used like a 'type'
Managed to fix it, by defining them as properties inside of that class -

Code - C#: [Select]
  1. using System.Windows.Forms;
  2.  
  3. namespace LispDialogSample_Gile
  4. {
  5.   public partial class Dialog : Form
  6.   {
  7.     public string TextValue { get; }
  8.     public int NumericValue { get; }
  9.     public Dialog()
  10.     {
  11.       InitializeComponent();
  12.       // the following properties can be set in the [Design] tab
  13.       btnOk.DialogResult = DialogResult.OK;
  14.       btnCancel.DialogResult = DialogResult.Cancel;
  15.      
  16.     }
  17.    
  18.     private void btnOk_Click(object sender, EventArgs e)
  19.     {
  20.       TextValue = textBox1.Text;
  21.       NumericValue = (int)numericUpDown1.Value;
  22.     } // btnOk_Click
  23.   }
  24. }


Learned alot from your posts.. and sorry for my late reply (took me some time to digest).

If you guys don't mind an additional question appeared:
As you probably guessed I'm a lisp guy, that used DCL to collect user inputs.
But the main reason for me to decide a switch, aside of all the fancy control events and the unique form design was because of some of the controls, that DCL doesn't seem to have.
I'm talking about the datagridview control, that would build a string[,] type of array, or in LISP I'd call it matrix list of strings.
In other words prompting user with a table (which is nearly impossible to do with DCL).

So my question is how would you pass a string[,] array to the ResultBuffer(); to the LispDataType enum? (say if I initially constructed the string[,] from the DGV's cell values)

Sorry for asking again (don't want to spam the .NET forum section with LISP-related question threads).

« Last Edit: October 04, 2018, 05:12:47 PM by Grrr1337 »
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: Defining a LISP function
« Reply #6 on: October 05, 2018, 12:08:25 AM »
As you probably guessed I'm a lisp guy, that used DCL to collect user inputs.

also, don't forget about Open DCL, I think it has a nice grid control.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #7 on: October 05, 2018, 02:20:52 AM »
Although I got some errors on declaring the public variables from the dialog class -

Code - C#: [Select]
  1. public string TextValue => textBox1.Text;
  2. public int NumericValue => (int)numericUpDown1.Value;

This is C#6 (or later) syntax for property get statement.
You can also write this:
Code - C#: [Select]
  1. public string TextValue
  2. {
  3.     get { return textBox1.Text; }
  4. }
  5. public int NumericValue
  6. {
  7.     get { return (int)numericUpDown1.Value; }
  8. }
[/quote]
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #8 on: October 05, 2018, 02:47:10 AM »
So my question is how would you pass a string[,] array to the ResultBuffer(); to the LispDataType enum? (say if I initially constructed the string[,] from the DGV's cell values)

As AutoLISP only knows (linked) lists as data structure, you have to convert a 2 dimension array into a list, typically a list of sublists where each sublist represent a row.

Code - C#: [Select]
  1.         [LispFunction("foo")]
  2.         public ResultBuffer Foo(ResultBuffer resbuf)
  3.         {
  4.             var array = new string[2, 3]
  5.             {
  6.                 {"a", "b", "c" },
  7.                 {"d", "e", "f" }
  8.             };
  9.  
  10.             return Array2dToList(array);
  11.         }
  12.  
  13.         private ResultBuffer Array2dToList(string[,] array)
  14.         {
  15.             var retVal = new ResultBuffer();
  16.             for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
  17.             {
  18.                 retVal.Add(new TypedValue((int)LispDataType.ListBegin));
  19.                 for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
  20.                 {
  21.                     retVal.Add(new TypedValue((int)LispDataType.Text, array[i, j]));
  22.                 }
  23.                 retVal.Add(new TypedValue((int)LispDataType.ListEnd));
  24.             }
  25.             return retVal;
  26.         }
Speaking English as a French Frog

estanima

  • Guest
Re: Defining a LISP function
« Reply #9 on: October 05, 2018, 01:18:07 PM »
Tried that as well when I started.  Dive in friend, C#.net is easier than lisp (to me) once the curve is over :)

Hey .NET developers,
I wanted to ask you if its possible to define a LISP function via C#/.NET , by providing the actual LISP code ?
I mean having the LISP code inside of the .NET code and writing it+defining it on-the-fly.

So basically like passing this into VLIDE's console and defining it:

Code - Auto/Visual Lisp: [Select]
  1. (defun C:test ( / str )
  2.   (if (setq str (getstring "\nType something: "))
  3.     (alert str)
  4.   )
  5.   (princ)
  6. )

Second question: how can you call the LISP function itself, again via .NET ?

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #10 on: October 05, 2018, 03:58:18 PM »

As you probably guessed I'm a lisp guy, that used DCL to collect user inputs.

also, don't forget about Open DCL, I think it has a nice grid control.

Ah, right. I know its more lisp-friendly - but still decided to try out .NET :)



Although I got some errors on declaring the public variables from the dialog class -
Code - C#: [Select]
  1. public string TextValue => textBox1.Text;
  2. public int NumericValue => (int)numericUpDown1.Value;

This is C#6 (or later) syntax for property get statement.


I see, now I understand why I get random errors from VS when I copy-paste C# samples from stackoverflow.




As AutoLISP only knows (linked) lists as data structure, you have to convert a 2 dimension array into a list, typically a list of sublists where each sublist represent a row.

Code - C#: [Select]
  1.  { code }

I saw the .ListBegin and .ListEnd enums but wasn't sure how they must be used -
For me that example is golden, Thanks gile!



Tried that as well when I started.  Dive in friend, C#.net is easier than lisp (to me) once the curve is over :)

I don't know why, but this OOP is dang hard for me (although not impossible).
When I get exceptions that I cannot self-explain (even after reading the exception message),
then I seek for examples and perform the american saying "monkey see, monkey do"(or monkey copy), until I start to get why its like that.


For instance now (playing for hours) I've assembled this from gile's codes and expected that it would work...

Code - C#: [Select]
  1. // Basically this is an attempt to use the 'Array2dToList' method within gile's 'GetDataFromDialog' example (and the 2dArray is built from a dataGridView1 control)
  2. public class LispFunctions
  3. {
  4.   [LispFunction("GetDataFromDGV")]
  5.   public static ResultBuffer GetDataFromDGV(ResultBuffer resbuf)
  6.   { // No matter what I do, I get this exception: "Object reference not set to an instance of an object."
  7.     var array = new string[2, 3]
  8.     {
  9.       {"a", "b", "c" },
  10.       {"d", "e", "f" }
  11.     };
  12.     var dialog = new Dialog();
  13.     var result = AcAp.ShowModalDialog(dialog);
  14.     if (result != DialogResult.OK)
  15.     return null;
  16.     // return Array2dToList(array); // if I decide to return this, then I would get the actual array
  17.     return Array2dToList(dialog.arrayDGV); // I would get null everytime (but I should get something?? - check the dialog class)
  18.    
  19.   }
  20.  
  21.   private static ResultBuffer Array2dToList(string[,] array)
  22.   {
  23.     var retVal = new ResultBuffer();
  24.     for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
  25.     {
  26.       retVal.Add(new TypedValue((int)LispDataType.ListBegin));
  27.       for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
  28.       {
  29.         retVal.Add(new TypedValue((int)LispDataType.Text, array[i, j]));
  30.       }
  31.       retVal.Add(new TypedValue((int)LispDataType.ListEnd));
  32.     }
  33.     return retVal;
  34.   }
  35.  
  36. }



Code - C#: [Select]
  1. public partial class Dialog : Form // the Form contains two buttons for ok and cancel, and a datagridview with few columns, so the user could input in a table
  2. {
  3.   public string[,] arrayDGV { get; }
  4.  
  5.   public Dialog()
  6.   {
  7.     InitializeComponent();
  8.     // the following properties can be set in the [Design] tab
  9.     dataGridView1.AllowUserToAddRows = true;
  10.     dataGridView1.AllowUserToDeleteRows = true;
  11.     btnOk.DialogResult = DialogResult.OK;
  12.     btnCancel.DialogResult = DialogResult.Cancel;
  13.   }
  14.  
  15.   private void btnOk_Click(object sender, EventArgs e)
  16.   {
  17.    
  18.     // https://stackoverflow.com/questions/25746659/how-to-get-datagridview-data-in-a-2d-array/25747273
  19.     // datagridview to a 2D array
  20.     string[,] arrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count]; // first of all create a 2D array varialble and count the rows and //column of data grid view
  21.     foreach (DataGridViewRow row in dataGridView1.Rows)
  22.     {
  23.       int i = row.Index;
  24.       foreach (DataGridViewColumn col in dataGridView1.Columns)
  25.       {
  26.         int j = col.Index;
  27.         string itm = dataGridView1.Rows[i].Cells[j].Value.ToString();
  28.         if (itm == null || itm == String.Empty || itm.Trim().Length == 0) // I've included this, just to make sure I would get something
  29.         arrayDGV[row.Index, col.Index] = "null";
  30.         else
  31.         arrayDGV[row.Index, col.Index] = itm;
  32.       }
  33.     }
  34.    
  35.   } // btnOk_Click
  36. }

(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #11 on: October 05, 2018, 05:23:09 PM »
Typically you do not handle the Click event for the OK and Cancel buttons, you only set their DialogResult property.
As the calling LispFunction creates an instance of the dialog, it can access to all its public instance members (properties and methods) and it's a better practice to expose the data this way.

Code - C#: [Select]
  1.     public partial class Dialog : Form
  2.     {
  3.         /// <summary>
  4.         /// Creates a new instance of Dialog
  5.         /// </summary>
  6.         public Dialog()
  7.         {
  8.             InitializeComponent();
  9.  
  10.             // the following properties can be set in the [Design] tab
  11.             dataGridView1.AllowUserToAddRows = true;
  12.             dataGridView1.AllowUserToDeleteRows = true;
  13.             btnOk.DialogResult = DialogResult.OK;
  14.             btnCancel.DialogResult = DialogResult.Cancel;
  15.         }
  16.  
  17.         /// <summary>
  18.         /// Returns the DataGridView contents as a 2D array
  19.         /// </summary>
  20.         /// <returns>A 2D array</returns>
  21.         public string[,] GridToArray()
  22.         {
  23.             int numRows = dataGridView1.RowCount - 1;
  24.             int numColumns = dataGridView1.ColumnCount;
  25.             var array = new string[numRows, numColumns];
  26.             for (int i = 0; i < numRows; i++)
  27.             {
  28.                 for (int j = 0; j < numColumns; j++)
  29.                 {
  30.                     var value = dataGridView1.Rows[i].Cells[j].Value;
  31.                     array[i, j] = value == null ? "null" : value.ToString();
  32.                 }
  33.             }
  34.             return array;
  35.         }
  36.     }

Code - C#: [Select]
  1.         [LispFunction("GetDataFromDDV")]
  2.         public ResultBuffer GetDataFromDGV(ResultBuffer resbuf)
  3.         {
  4.             using (var dialog = new Dialog())
  5.             {
  6.                 var result = AcAp.ShowModalDialog(dialog);
  7.                 if (result != DialogResult.OK)
  8.                     return null;
  9.                 return Array2dToList(dialog.GridToArray());
  10.             }
  11.         }
  12.  
  13.         private ResultBuffer Array2dToList(string[,] array)
  14.         {
  15.             var retVal = new ResultBuffer();
  16.             for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
  17.             {
  18.                 retVal.Add(new TypedValue((int)LispDataType.ListBegin));
  19.                 for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
  20.                 {
  21.                     retVal.Add(new TypedValue((int)LispDataType.Text, array[i, j]));
  22.                 }
  23.                 retVal.Add(new TypedValue((int)LispDataType.ListEnd));
  24.             }
  25.             return retVal;
  26.         }
Speaking English as a French Frog

Grrr1337

  • Swamp Rat
  • Posts: 812
Re: Defining a LISP function
« Reply #12 on: October 06, 2018, 07:03:55 AM »
Typically you do not handle the Click event for the OK and Cancel buttons, you only set their DialogResult property.
 
I understand now, unlike DCL to store the returned value from the dialog via (start_dialog) function,
here the result is just kept as a property of the dialog object, and can be accessed anytime.


As the calling LispFunction creates an instance of the dialog, it can access to all its public instance members (properties and methods) and it's a better practice to expose the data this way.
   
Your example is working (as expected)!

By the way everything works fine in my next attempt.. although I don't understand why I got "Object reference not set to an instance of an object." exception (in the previous code, before your reply).
I was switching the ResultBuffer Methods from static to non-static,
aswell providing the initially declared var array = new string[2, 3] { {"a", "b", "c" }, {"d", "e", "f" } }; if the fault was in dialog.arrayDGV


Based on your example code, I find it more human-readable to process the public properties of a class, via private methods.. and then exposing them -

Code - C#: [Select]
  1. // This is just another practice, writing all from scratch and based on gile's example
  2. public class LispFunctions
  3. {
  4.  
  5.   [LispFunction("GetDataFromDGV")]
  6.   public ResultBuffer GetDataFromDGV(ResultBuffer resbuf)
  7.   {
  8.  
  9.     // I still don't get why I had this exception in my previous attempt: "Object reference not set to an instance of an object."
  10.     // regardless if I used "var dialog = new Dialog();" or "using (var dialog = new Dialog())", doesn't seem to be a reason for this error
  11.  
  12.     // using (var dialog = new Dialog())
  13.     // {
  14.     var dialog = new Dialog();
  15.     var result = AcAp.ShowModalDialog(dialog);
  16.     if (result != DialogResult.OK)
  17.     return null;
  18.     return Array2dToList(dialog.arrayDGV);
  19.     // } // using
  20.  
  21.   } // public ResultBuffer
  22.  
  23.   #region Private Methods for the LispFunctions class
  24.  
  25.   private ResultBuffer Array2dToList(string[,] array)
  26.   {
  27.       if (array == null || array.Length == 0)
  28.       return null;
  29.       else
  30.       {
  31.           var retVal = new ResultBuffer();
  32.           for (int i = array.GetLowerBound(0); i <= array.GetUpperBound(0); i++)
  33.           {
  34.               retVal.Add(new TypedValue((int)LispDataType.ListBegin));
  35.               for (int j = array.GetLowerBound(1); j <= array.GetUpperBound(1); j++)
  36.               {
  37.                   retVal.Add(new TypedValue((int)LispDataType.Text, array[i, j]));
  38.               }
  39.               retVal.Add(new TypedValue((int)LispDataType.ListEnd));
  40.           }
  41.           return retVal;
  42.       }
  43.   }
  44.  
  45.   #endregion
  46.  
  47. }
  48.  

Code - C#: [Select]
  1. // This is just another practice, writing all from scratch and based on gile's example
  2. public partial class Dialog : Form
  3. {
  4.   // public string[,] arrayDGV { get; }
  5.  
  6.   public string[,] arrayDGV
  7.   {
  8.     get { return GridToArray(dataGridView1); }
  9.   }
  10.   public Dialog()
  11.   {
  12.     InitializeComponent();
  13.     dataGridView1.AllowUserToAddRows = true;
  14.     dataGridView1.AllowUserToDeleteRows = true;
  15.     btnOk.DialogResult = DialogResult.OK;
  16.     btnCancel.DialogResult = DialogResult.Cancel;
  17.   } // public Dialog()
  18.  
  19.   #region private Methods for Dialog class
  20.  
  21.   /// <summary>
  22.   /// Returns the DataGridView contents as a 2D array
  23.   /// </summary>
  24.   /// <returns>A 2D array</returns>
  25.   /// this version crashes if the DataGridView is empty
  26.   private string[,] GridToArray( DataGridView DGV ) // gile's subfoo version
  27.   {
  28.     int numRows = DGV.RowCount - 1;
  29.     int numColumns = DGV.ColumnCount;
  30.     var array = new string[numRows, numColumns];
  31.     for (int i = 0; i < numRows; i++)
  32.     {
  33.       for (int j = 0; j < numColumns; j++)
  34.       {
  35.         var value = DGV.Rows[i].Cells[j].Value;
  36.         array[i, j] = value == null ? "" : value.ToString();
  37.       }
  38.     }
  39.     return array;
  40.   } //  public string[,] GridToArray
  41.  
  42.  
  43.   // https://stackoverflow.com/questions/25746659/how-to-get-datagridview-data-in-a-2d-array/25747273
  44.   // datagridview to a 2D array
  45.   // this version crashes with fatal error, if the DataGridView is empty
  46.   private string [,] DataGridViewTo2Darray ( DataGridView DGV )
  47.   {
  48.     var arrayDGV = new string[DGV.Rows.Count, DGV.Columns.Count]; // first of all create a 2D array varialble and count the rows and //column of data grid view
  49.     foreach (DataGridViewRow row in DGV.Rows)
  50.     {
  51.       int i = row.Index;
  52.       foreach (DataGridViewColumn col in DGV.Columns)
  53.       {
  54.         int j = col.Index;
  55.         string itm = DGV.Rows[i].Cells[j].Value.ToString();
  56.         if (itm == null || itm == String.Empty || itm.Trim().Length == 0) // I've included this, just to make sure I would get something
  57.         arrayDGV[row.Index, col.Index] = "";
  58.         else
  59.         arrayDGV[row.Index, col.Index] = itm;
  60.       }
  61.     }
  62.     return arrayDGV;
  63.   } // private string [,] DataGridViewTo2Darray
  64.  
  65.   #endregion
  66.  
  67. }

Thank you once again, gile!
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)
vevo.bg

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #13 on: October 06, 2018, 01:25:23 PM »
Quote
although I don't understand why I got "Object reference not set to an instance of an object." exception (in the previous code, before your reply).

Because the public property 'Dialog.arrayDGV' is never instantiated.
You use a local variable also named arrayDGV in the btnOk_Click() method, but this variable is local to the method.
You should use the common naming convention for C#: use PascalCase for classes, methods, properties and camelCase for local variables and method parameters (see this topic).

Code - C#: [Select]
  1.     public partial class Dialog : Form // the Form contains two buttons for ok and cancel, and a datagridview with few columns, so the user could input in a table
  2.     {
  3.       public string[,] ArrayDGV { get; private set; } // the property is read only and set within the class by a method other tahn a constructor
  4.      
  5.       public Dialog()
  6.       {
  7.         InitializeComponent();
  8.         // the following properties can be set in the [Design] tab
  9.         dataGridView1.AllowUserToAddRows = true;
  10.         dataGridView1.AllowUserToDeleteRows = true;
  11.         btnOk.DialogResult = DialogResult.OK;
  12.         btnCancel.DialogResult = DialogResult.Cancel;
  13.       }
  14.      
  15.       private void btnOk_Click(object sender, EventArgs e)
  16.       {
  17.      
  18.         // https://stackoverflow.com/questions/25746659/how-to-get-datagridview-data-in-a-2d-array/25747273
  19.         // datagridview to a 2D array
  20.         //string[,] arrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count]; // first of all create a 2D array varialble and count the rows and //column of data grid view
  21.  
  22.         // instead of using a local variable, directly instantiate and set the ArrayDGV property
  23.         ArrayDGV = new string[dataGridView1.Rows.Count, dataGridView1.Columns.Count];
  24.         foreach (DataGridViewRow row in dataGridView1.Rows)
  25.         {
  26.           int i = row.Index;
  27.           foreach (DataGridViewColumn col in dataGridView1.Columns)
  28.           {
  29.             int j = col.Index;
  30.             string itm = dataGridView1.Rows[i].Cells[j].Value.ToString();
  31.             if (itm == null || itm == String.Empty || itm.Trim().Length == 0) // I've included this, just to make sure I would get something
  32.             arrayDGV[row.Index, col.Index] = "null";
  33.             else
  34.             arrayDGV[row.Index, col.Index] = itm;
  35.           }
  36.         }
  37.      
  38.       } // btnOk_Click
  39.     }
Speaking English as a French Frog

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: Defining a LISP function
« Reply #14 on: October 06, 2018, 01:46:47 PM »
Two other remarks about "good practices" (which I sometimes forget to respect):
- always use a using statement to create an instance of a modal dialog ('closing' the modal dialog only hides it but does not call Close / Dispose so it remains in memory during all the session).
- Always prefer to use a static method for commands or LISP functions when an instance method is not absolutely necessary.
« Last Edit: October 06, 2018, 01:50:00 PM by gile »
Speaking English as a French Frog