snippetcppMinor
Did I convert this C++ class to Common Lisp correctly?
Viewed 0 times
thislispconvertdidcorrectlyclasscommon
Problem
Here is the original C++ class I converted to Lisp at the very bottom of this post.
It is a class from a C++ Neural Network I am converting to Common Lisp. The entire 400 line Neural Net is here.
Below is the conversion I came up with. Based on the Neural Network I linked above, can anyone tell me if I converted the C+ class, above, correctly with the following Lisp code? I'd rather have a little assurance before I start converting the entire 400 lines.
```
(defclass training-data ()
((training-data-stream :reader training-data-stream
:writer |set training data stream (using a funky name)|
:initarg :training-data-stream))
)
;;; initialize-instance :after -- This is your 'constructor' code.
(defmethod initialize-instance :after ((td training-data)
&key
training-data-stream
&allow-other-keys)
;; Initialize the slot.
;; Note that 'opening' a stream, as it is implied by the C++ class
;; definiton and leaking the open stream handle is very impolite.
(let ((is (etypecase training-data-stream
((string pathname) (open training-data-stream :direction :input))
(stream (if (input-stream-p training-data-stream)
training-data-stream
(error "Not an input stream.")))))
)
(|set training data stream (using a funky name)| is td)
))
(defgeneric is-eof (td)
(:method ((td training-data))
;; Are you sure you want this me
class TrainingData
{
public:
TrainingData(const string filename);
bool isEof(void) { return m_trainingDataFile.eof(); }
void getTopology(vector &topology);
// Returns the number of input values read from the file:
unsigned getNextInputs(vector &inputVals);
unsigned getTargetOutputs(vector &targetOutputVals);
private:
ifstream m_trainingDataFile;
};It is a class from a C++ Neural Network I am converting to Common Lisp. The entire 400 line Neural Net is here.
Below is the conversion I came up with. Based on the Neural Network I linked above, can anyone tell me if I converted the C+ class, above, correctly with the following Lisp code? I'd rather have a little assurance before I start converting the entire 400 lines.
```
(defclass training-data ()
((training-data-stream :reader training-data-stream
:writer |set training data stream (using a funky name)|
:initarg :training-data-stream))
)
;;; initialize-instance :after -- This is your 'constructor' code.
(defmethod initialize-instance :after ((td training-data)
&key
training-data-stream
&allow-other-keys)
;; Initialize the slot.
;; Note that 'opening' a stream, as it is implied by the C++ class
;; definiton and leaking the open stream handle is very impolite.
(let ((is (etypecase training-data-stream
((string pathname) (open training-data-stream :direction :input))
(stream (if (input-stream-p training-data-stream)
training-data-stream
(error "Not an input stream.")))))
)
(|set training data stream (using a funky name)| is td)
))
(defgeneric is-eof (td)
(:method ((td training-data))
;; Are you sure you want this me
Solution
Code review - mostly style
You did a litteral translation from C++ to CL that seems good. I have some stylistic issues with it:
-
I guess
-
You can define a
Visibility is handled at package level in CL.
-
Your
Then...
So far, so good; just rename
Your own comment highlights that
Besides, "impolite" is not the word I would have used, because it is not accurate (it sounds as-if you ignore, or side-step, the real problems behind leaking resources).
Hereafter, your
In other word,
Moreover, you changed the original behavior by allowing the constructor to be initialized with streams, and not only strings (was it necessary?).
Note that instead of
Your objective
I'm trying to learn Neural Networks by converting a C++ nn to Lisp
Learning high-level concepts like neural networks using an implementation like the one you linked is not very effective, in my opinion.
Even though you have difficulties with C++, this is not the language that prevents you from understanding neural networks; for example:
Those are just math operations, and the fact that they are written in C++ is not what makes them difficult to grasp. In fact, I never really studied neural networks and all the above code is cryptic, even though I am familiar with the language.
I doubt that rewriting it in CL will help you.
What you need first is a book and tutorials for high-level concepts.
Experiment with a easy-to-use library, like for example PyBrain, which has a good documentation.
Maybe when you have a good understanding of neural networks, you can go back to the linke
You did a litteral translation from C++ to CL that seems good. I have some stylistic issues with it:
(defclass training-data ()
((training-data-stream :reader training-data-stream
:writer |set training data stream (using a funky name)|
:initarg :training-data-stream)))-
I guess
|set training data stream (using a funky name)| is an attempt at obfuscating the writer method, so that you can have a kind of "private" setter. Don't do that.:writeris optional: if you don't specify it, you won't generate a specific setter for your slot. You can always useslot-valueto alter a slot. However, I don't recommend this appropach.
-
You can define a
:accessor method, which acts as both a reader and a writer method (resp. (my-accessor instance) and (setf (my-accessor instance) value)). Since your C++ field is private and has no public getter/setter, this is an appropriate approach:- Define only an accessor, say
training-data-stream, and
- Do not export the
training-data-streamsymbol from your package.
Visibility is handled at package level in CL.
-
Your
initarg could be made shorter: it is used when making an instance of a training-data class, so :training-data-stream is redundant, just use :input-stream (:stream would be too vague).Then...
;;; initialize-instance :after -- This is your 'constructor' code.
(defmethod initialize-instance :after ((td training-data)
&key
training-data-stream
&allow-other-keys)
;; Initialize the slot.So far, so good; just rename
training-data-stream as input-stream.;; Note that 'opening' a stream, as it is implied by the C++ class
;; definiton and leaking the open stream handle is very impolite.Your own comment highlights that
- the original C++ class might not be good at following C++ best practices (RAII, for example), and
- you are doing a verbatim clone of the C++ class instead of having a more idiomatic CL approach. Since you are just copying an existing codebase in order to understand it, it is not so bad. Maybe you could try to refactor it later, as an exercise.
Besides, "impolite" is not the word I would have used, because it is not accurate (it sounds as-if you ignore, or side-step, the real problems behind leaking resources).
Hereafter, your
let binding is useless:(let ((is (etypecase training-data-stream
((string pathname) (open training-data-stream :direction :input))
(stream (if (input-stream-p training-data-stream)
training-data-stream
(error "Not an input stream.")))))
)
(|set training data stream (using a funky name)| is td)
))In other word,
(let ((x (something))) (use x)) could simply be (use (something)).Moreover, you changed the original behavior by allowing the constructor to be initialized with streams, and not only strings (was it necessary?).
Note that instead of
etypecase, which is good, you could also let initialize-instance call a new generic method, say initialize, that dispatches on the type of the stream, either string or stream (we can't dispatch on &key arguments, otherwise initialize-instance would suffice).(setf (training-data-stream td)
(etypecase input-stream
((string pathname) (open input-stream :direction :input))
(stream (if (input-stream-p training-data-stream)
training-data-stream
(error "Not an input stream."))))))Your objective
I'm trying to learn Neural Networks by converting a C++ nn to Lisp
Learning high-level concepts like neural networks using an implementation like the one you linked is not very effective, in my opinion.
Even though you have difficulties with C++, this is not the language that prevents you from understanding neural networks; for example:
Layer &outputLayer = m_layers.back();
m_error = 0.0;
for (unsigned n = 0; n < outputLayer.size() - 1; ++n) {
double delta = targetVals[n] - outputLayer[n].getOutputVal();
m_error += delta * delta;
}
m_error /= outputLayer.size() - 1; // get average error squared
m_error = sqrt(m_error); // RMSThose are just math operations, and the fact that they are written in C++ is not what makes them difficult to grasp. In fact, I never really studied neural networks and all the above code is cryptic, even though I am familiar with the language.
I doubt that rewriting it in CL will help you.
What you need first is a book and tutorials for high-level concepts.
Experiment with a easy-to-use library, like for example PyBrain, which has a good documentation.
Maybe when you have a good understanding of neural networks, you can go back to the linke
Code Snippets
(defclass training-data ()
((training-data-stream :reader training-data-stream
:writer |set training data stream (using a funky name)|
:initarg :training-data-stream)));;; initialize-instance :after -- This is your 'constructor' code.
(defmethod initialize-instance :after ((td training-data)
&key
training-data-stream
&allow-other-keys)
;; Initialize the slot.;; Note that 'opening' a stream, as it is implied by the C++ class
;; definiton and leaking the open stream handle is very impolite.(let ((is (etypecase training-data-stream
((string pathname) (open training-data-stream :direction :input))
(stream (if (input-stream-p training-data-stream)
training-data-stream
(error "Not an input stream.")))))
)
(|set training data stream (using a funky name)| is td)
))(setf (training-data-stream td)
(etypecase input-stream
((string pathname) (open input-stream :direction :input))
(stream (if (input-stream-p training-data-stream)
training-data-stream
(error "Not an input stream."))))))Context
StackExchange Code Review Q#40212, answer score: 3
Revisions (0)
No revisions yet.