Medical education is something that is constantly changing. In order to keep up with the latest changes, it is important for doctors and other medical professionals to stay informed about new research and techniques. One way to do this is by participating in online med ed 531 courses. These courses are designed to help professionals learn about the latest advancements in their field and how they can be applied in a clinical setting. By completing an online med ed 531 course, you can be sure that you are up-to-date on the latest medical information. "Medical education is something that is constantly changing.
You can find info about the type of form you intend to fill out in the table. It can show you just how long it takes to fill out online med ed 531, what parts you will have to fill in, etc.
Question | Answer |
---|---|
Form Name | Online Med Ed 531 |
Form Length | 8 pages |
Fillable? | No |
Fillable fields | 0 |
Avg. time to fill out | 2 min |
Other names | onlinemeded pdf download, onlinemeded whiteboard book pdf, onlinemeded notes pdf google drive, onlinemeded videos google drive |
MEDED 531 Lecture Notes
for
Monday,
Notes taken by Jessica Wilkinson, edited by Ira Kalet
1 Data Abstraction
So far in this class, we have seen ways in which we can represent complex data as lists. We have also seen how we can combine these basic data items in more complex ways; for example, we have seen how we can represent student projects as association lists, and store several such items in a list of student projects.
Patient records can be structured as lists. Each record contains the patient name, address, telephone numbers, date of birth, and other relevant information grouped together in a complex structure:
((simone jones)
((1234 boylston street) seattle wa 98102) ((home
We could then create a simple database of these records and perform operations such as searching for a patient, adding a new patient, etc. If this were a small system that only a single person was responsible for, it might be feasible to create database operation functions that relied directly on the underlying data structures. For instance, the patient’s name appears first in the record, so that knowledge might be used to create a search function that evaluated only the first element of each record.
Now imagine what would happen if a unique patient ID number was added to each record as the first element. What would happen to our initial name search function? By tying the database operation functions directly to the underlying data structures, changes in the records themselves would result in
Instead of searching for a match on the first element of a patient record, what we really should do is match on a name or ID number, regardless of how that information is actually stored. Data abstraction is the process by which internal implementation details (such as underlying data structures) are abstracted away and only relevant information is presented. In the patient record example, all we want to access is the patient’s name; we don’t want to concern ourselves with details such as where the name appears in the underlying list construct.
In general terms, abstraction refers to ignoring the details and focusing on the essentials. Abstraction gives us the ability to encapsulate and isolate design and execution information.
Student Question: What is the difference between procedural and data abstraction?
1
Procedural abstraction hides operational details, such as the exact method needed to get something out of a complex data structure. Data abstraction is a method of hiding the details of the way data is structured. Both are two faces of the same idea.
2 Accessor Functions
We want to hide specific implementation details of the underlying structures that make up our patient records, yet still be able to access the data. Accessor functions allow you to do just that; they define a way to access data while abstracting away implementation details.
Here is an example of an accessor function for retrieving a name from a patient record:
(defun name (pt) (first patient))
If we wanted to get the name of a specific patient, all that is required now is to call name for that patient; there is no need to be concerned with underlying record organization.
Data and procedural abstraction allow us to create abstraction layers. Abstraction layers are created when information about underlying structures and operations are hidden behind abstraction boundaries. For ex- ample, an abstraction layer is created by the accessor function name. An abstraction boundary exists be- tween the outside environment and the patient record at this point; if something needs access to the name field in the record, it goes through the name function instead of directly accessing the record itself. So, instead of needing to know the details of the record, we only need to understand the name interface.
Abstraction layers can be built up, with each relying only on the one immediately below it. For example, we can build on our initial name function to access a patient’s first name:
(defun
Note that
2.1 Why use abstraction?
Once you create abstraction layers, you can change the underlying data structures as long as you keep func- tional interfaces consistent. For example, we could change how names are stored in our patient records, and as long as we changed the name function accordingly, nothing beyond that abstraction boundary would be affected.
Abstraction layers allow us to build very complex systems while keeping design simple. This is one of the most important concepts to take away from this class.
2.2 Accessor functions can do some work
Accessor functions can be used to provide useful data transformations as well as basic retrieval. For example, we can define a function called
2
patient record into formatted output.
(defun
(second (if type
(assoc type numbers)) (first numbers))))
Accessor functions can have varying degrees of complexity, but all are related in that they access underlying data structures while hiding unnecessary organizational details.
3 Constructors
Constructors hide details of creating new data objects. For example, here is a constructor to create patient records:
(defun
(list name address city state zip (list (list home
(list work
birthdate diagnosis physician))
Note that to create a new patient record only requires that
4 Transforming procedures
Transforming procedures are used to change data into some other form. For example, it is useful to have a function that takes a list and applies a transformation to each element. We don’t want to be concerned with how the list elements are organized; we just want to have a function that allows us to make changes, not worry about the details of how it does so.
3
4.1 Transforming procedures for lists are recursive
Imagine we had a list that represented a single DNA strand, and wanted to transform it to represent the complimentary strand. It is easy to see how to do this recursively:
Solve a small part of the problem: take the first base off the DNA strand (get the first element of the list) and transform it into its complementary base. Construct a new list/strand of the transformed bases.
Apply the transformation process to the rest of the strand/list.
Stop when the list is empty, and all bases have been transformed.
We can generalize this example into a basic template that all list transformation procedures take:
(defun
(cons
In all cases, we want to stop the transformation process when a list is empty. Otherwise, we start with the simplest base case: apply the transformation to the first element in the list. This is represented in the above template as
Note that instead of needing to store temporary results of single transformations, recursion allows you to easily change a single element and then stick it on the front of a new list.
Student Question: Why do we need recursion in transformation procedures?
We use recursion to solve the problem of transforming an entire list because it is very easy to transform a single element and then apply the function to the rest of the list. For example, imagine that we want to transform the (a b c d) into (1 2 3 4).
Here is a diagram of how the transformation would work, using the template given above. Note that at each level, we transform the first element then recurse on the remaining sublist:
( |
a |
b |
c |
d |
) |
|
|
|
( |
1✁ |
( |
b |
c |
d |
) |
) |
|
|
( |
1 |
2✁ |
( |
c |
d |
) |
) |
|
|
( |
1 |
2 |
3✁ |
( |
d |
) ) |
|
|
|
( |
1 |
2 |
3 |
4✁ |
) |
4
4.2 Transforming procedure for DNA replication
Applying the template above, we can get a transformation procedure for DNA replication:
(defun
(cons
(defun
((eql x c) g) ((eql x a) t) ((eql x t) a) (t (error
"your dna is screwy"))))
Student Question: What does the error function do?
error is included in Common Lisp. It displays an error message and causes the interpreter to enter into a break loop. At this point, the user can issue debugging commands. Here is an example of how the above function responds to normal vs. error conditions:
>
T
>
C
>
Error: your dna is screwy
Note that error requires an error message string as its single input.
4.2.1Generalizing the DNA transformation procedure
Note that defining
>
5
And here is an example of a more generalized
(defun
(cons (funcall fn (first input))
Now we have a function that takes any kind of single element transformer and applies it to any kind of list. This kind of function generalization is good programming practice because it ensures code
Student Question: What if people using our
This is a very important question. What we are really asking is “how can we
When you write a function, you can’t guess about what people might want to happen, or intend to happen. You need to have concrete,
5 Transform functions are generalized in mapcar
The idea of transforming functions is such a common and useful one that a completely generalized one was included in lisp: mapcar. Here is an example of using mapcar to transform a DNA strand:
>(mapcar
mapcar maps a transformation function onto each element in a list. Note that it returns a new list; it does not actually modify the original one. mapcar has no side effects.
Here is another example using mapcar to calculate square roots:
>(mapcar #’sqrt ’(1 2 3 4)) (1 1.4142135 1.7320508 2)
Student Question: Can mapcar be used on nested lists?
mapcar itself works on top level elements only. For instance, the following counts the number of elements in each sublist:
>(mapcar #’length ’((a b) nil (c g w) (e) (f))) (2 0 3 1 1)
6
If you want to operate on the sublists themselves, you can do so by specifying an appropriate element transformation procedure that handled underlying organizational details. It can then be passed to mapcar which will then apply it to each
5.1 You can write your own version of mapcar
Even though mapcar is included in Common Lisp, you could write your own version if necessary:
(defun
(cons (funcall fn (first s))
mapcar is an example of a programming clich´e; transformation procedures for lists are so common they become clich´es. As you write your own programs, you may find some patterns continuously reappear- ing. Instead of
Relying on template functions, whether
6 Functions as arguments
We have seen how functions may be passed as inputs to other functions. For instance, we were able to pass the function sqrt to mapcar in an earlier example.
The function funcall is used to call another function. It is most often used inside a function to call a function passed in as input.
6.1funcall vs. apply
funcall requires you to know the exact number of arguments a function takes. For something like the + function which takes a variable number of arguments, specifying exact inputs is difficult. apply, on the other hand, takes a list of arguments and applies a function to them, so you do not need to consider exact inputs.
7 Anonymous functions
Anywhere a named function is used, an anonymous function can be used instead. Anonymous functions are implemented by lambda expressions, which allow you to specify what operations need to be performed without having to explicitly defun a named function.
Anonymous functions can use information from the surrounding environment in a way that named functions cannot. We saw an example of how this feature is used by the Prism pen plotter output code. In it, graphic
7
primitives are sorted by color before drawing, to minimize the time spent on changing the pens within the plotter. Each plotter has different pens in different orders, so it became important to determine which plotter was being used. This was done by accessing the environment variable plt. Anonymous functions had to be used in order to do so.
Another example of using anonymous functions to access environment data was demonstrated in the
Lambda functions have the benefit of showing exactly what needs to be done at a certain code point. Thus, if you’re only going to use something once, it can be convenient and make code easier to read if you use an anonymous function. However, if you find that you are continuously
8 fibonacci and efficiency
Being able to break down problems and solve them using recursion is an extremely important concept. Re- cursion is a powerful tool, but we have seen some instances of recursive functions where the computational and system resources required by them make them slow and inefficient.
The most notable example we have seen of this has been the fibonacci function. As originally defined, it repeatedly computed the same values, causing severe slowdowns and high memory requirements. Tracing the original fibonacci function demonstrates exactly where these repeat calculations occur.
In class we saw a way to dramatically increase fibonacci’s efficiency by having it cache intermit- tent results instead of continuously recalculating them. The memoize function was used to produce a fibonacci version that did just that.
Intermediate values are stored in a hash table in the improved fibonacci function. Hash tables can be thought of as extensions of association lists, where each key is associated with a value. Hash table utilities are already built into lisp, so they are easily included.
The memo function uses a lambda expression to create a function that contains a hash table. Because the hash table needs to be accessed after it is created, it has to be “kept around” by creating reference to it, just as if it were a piece of data. This is what the memoize function does.
Thus when fibonacci is memoized, it gets a new function definition. This new definition includes caching abilities, and thus causes fibonacci to run much more efficiently.
8